Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: interop invariant checks #58

Merged
merged 1 commit into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,15 @@ Supersim allows developers to start multiple local evm nodes with one command, a
Supersim is a lightweight tool that simulates an interoperable Superchain environment locally. It does not require a complicated devnet setup and is run using cli commands with configuration options that fall back to sensible defaults if they are not specified. Each chain is an instance of [anvil](https://book.getfoundry.sh/reference/anvil/), though future versions may support other local testing tools.

## Getting started
### Installation
TODO
### Running Locally
1. build the binary by running:
```
go build cmd/main.go
```
2. start supersim in vanilla mode by running:
```
./main
```

## Features
### Vanilla mode
Expand Down
18 changes: 18 additions & 0 deletions anvil/anvil.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"io"
"math/big"
"os"
"os/exec"
"strconv"
Expand Down Expand Up @@ -283,7 +284,24 @@ func (a *Anvil) EthSendTransaction(ctx context.Context, tx *types.Transaction) e
return a.ethClient.SendTransaction(ctx, tx)
}

func (a *Anvil) EthBlockByNumber(ctx context.Context, blockHeight *big.Int) (*types.Block, error) {
return a.ethClient.BlockByNumber(ctx, blockHeight)
}

// subscription API
func (a *Anvil) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
return a.ethClient.SubscribeFilterLogs(ctx, q, ch)
}

func (a *Anvil) DebugTraceCall(ctx context.Context, txArgs config.TransactionArgs) (config.TraceCallRaw, error) {
var result config.TraceCallRaw
if err := a.rpcClient.CallContext(ctx, &result, "debug_traceCall", txArgs, "latest", map[string]interface{}{
"tracer": "callTracer",
"tracerConfig": map[string]interface{}{
"withLog": true,
},
}); err != nil {
return config.TraceCallRaw{}, err
}
return result, nil
}
34 changes: 34 additions & 0 deletions config/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package config
import (
"context"
"fmt"
"math/big"
"strings"

registry "github.com/ethereum-optimism/superchain-registry/superchain"
Expand All @@ -12,6 +13,7 @@ import (
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
)
Expand Down Expand Up @@ -96,6 +98,35 @@ type NetworkConfig struct {
L2Configs []ChainConfig
}

type TransactionArgs struct {
From common.Address `json:"from"`
To *common.Address `json:"to"`
Gas hexutil.Uint64 `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"`
Data hexutil.Bytes `json:"data"`
Value *hexutil.Big `json:"value"`
}

type TraceCallRaw struct {
Error *string `json:"error,omitempty"`
Type string `json:"type"`
From string `json:"from"`
To string `json:"to"`
Value string `json:"value"`
Gas string `json:"gas"`
GasUsed string `json:"gasUsed"`
Input string `json:"input"`
Output string `json:"output"`
Logs []*TraceCallRawLog `json:"logs"`
Calls []TraceCallRaw `json:"calls"`
}

type TraceCallRawLog struct {
Address common.Address `json:"address"`
Topics []common.Hash `json:"topics"`
Data string `json:"data"`
}

type Chain interface {
Name() string
Endpoint() string
Expand All @@ -109,7 +140,10 @@ type Chain interface {
EthGetCode(ctx context.Context, account common.Address) ([]byte, error)
EthGetLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error)
EthSendTransaction(ctx context.Context, tx *types.Transaction) error
EthBlockByNumber(ctx context.Context, blockHeight *big.Int) (*types.Block, error)

SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error)
DebugTraceCall(ctx context.Context, txArgs TransactionArgs) (TraceCallRaw, error)
}

// Note: The default secrets config is used everywhere
Expand Down
2 changes: 2 additions & 0 deletions contracts/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ out/
# Ignores development broadcast logs
!/broadcast
/broadcast/*/31337/
/broadcast/*/901/
/broadcast/*/902/
/broadcast/**/dry-run/

# Docs
Expand Down
Binary file added main
Binary file not shown.
73 changes: 73 additions & 0 deletions opsimulator/crossl2inbox.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package opsimulator

import (
_ "embed"
"errors"
"fmt"
"math/big"

"github.com/ethereum-optimism/optimism/op-service/predeploys"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/packages/contracts-bedrock/snapshots"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)

const (
eventExecutingMessage = "ExecutingMessage"
)

var (
ErrEventNotFound = errors.New("event not found")
)

type executingMessage struct {
MsgHash [32]byte
Identifier MessageIdentifier
}

type MessageIdentifier struct {
Origin common.Address
BlockNumber *big.Int
LogIndex *big.Int
Timestamp *big.Int
ChainId *big.Int
}

type crossL2Inbox struct {
Contract *batching.BoundContract
Abi abi.ABI
}

func NewCrossL2Inbox() *crossL2Inbox {
abi := snapshots.LoadCrossL2InboxABI()
return &crossL2Inbox{
Abi: *abi,
Contract: batching.NewBoundContract(abi, predeploys.CrossL2InboxAddr),
}
}

func (i *crossL2Inbox) decodeExecutingMessageLog(l *types.Log) (*executingMessage, error) {
if l.Address != i.Contract.Addr() {
return nil, nil
}
name, result, err := i.Contract.DecodeEvent(l)
if errors.Is(err, batching.ErrUnknownEvent) {
return nil, fmt.Errorf("%w: %v", ErrEventNotFound, err.Error())
} else if err != nil {
return nil, fmt.Errorf("failed to decode event: %w", err)
}
if name != eventExecutingMessage {
return nil, nil
}

msgHash := result.GetBytes32(0)
var messageIdentifier MessageIdentifier
result.GetStruct(1, &messageIdentifier)

return &executingMessage{
MsgHash: msgHash,
Identifier: messageIdentifier,
}, nil
}
Loading
Loading