Commit 5bd1f463 authored by clabby's avatar clabby

WIP: Optimize differential testing

parent 7bdcb0ff
......@@ -11,4 +11,3 @@ tmp-artifacts
deployments/mainnet-forked
deploy-config/mainnet-forked.json
test-case-generator/fuzz
.resource-metering.csv
......@@ -508,16 +508,15 @@ contract FFIInterface is Test {
uint256 _gasLimit,
bytes memory _data
) external returns (bytes32) {
string[] memory cmds = new string[](9);
cmds[0] = "node";
cmds[1] = "dist/scripts/differential-testing.js";
cmds[2] = "hashCrossDomainMessage";
cmds[3] = vm.toString(_nonce);
cmds[4] = vm.toString(_sender);
cmds[5] = vm.toString(_target);
cmds[6] = vm.toString(_value);
cmds[7] = vm.toString(_gasLimit);
cmds[8] = vm.toString(_data);
string[] memory cmds = new string[](8);
cmds[0] = "scripts/differential-testing/differential-testing";
cmds[1] = "hashCrossDomainMessage";
cmds[2] = vm.toString(_nonce);
cmds[3] = vm.toString(_sender);
cmds[4] = vm.toString(_target);
cmds[5] = vm.toString(_value);
cmds[6] = vm.toString(_gasLimit);
cmds[7] = vm.toString(_data);
bytes memory result = vm.ffi(cmds);
return abi.decode(result, (bytes32));
......@@ -531,16 +530,15 @@ contract FFIInterface is Test {
uint256 _gasLimit,
bytes memory _data
) external returns (bytes32) {
string[] memory cmds = new string[](9);
cmds[0] = "node";
cmds[1] = "dist/scripts/differential-testing.js";
cmds[2] = "hashWithdrawal";
cmds[3] = vm.toString(_nonce);
cmds[4] = vm.toString(_sender);
cmds[5] = vm.toString(_target);
cmds[6] = vm.toString(_value);
cmds[7] = vm.toString(_gasLimit);
cmds[8] = vm.toString(_data);
string[] memory cmds = new string[](8);
cmds[0] = "scripts/differential-testing/differential-testing";
cmds[1] = "hashWithdrawal";
cmds[2] = vm.toString(_nonce);
cmds[3] = vm.toString(_sender);
cmds[4] = vm.toString(_target);
cmds[5] = vm.toString(_value);
cmds[6] = vm.toString(_gasLimit);
cmds[7] = vm.toString(_data);
bytes memory result = vm.ffi(cmds);
return abi.decode(result, (bytes32));
......@@ -552,14 +550,13 @@ contract FFIInterface is Test {
bytes32 _messagePasserStorageRoot,
bytes32 _latestBlockhash
) external returns (bytes32) {
string[] memory cmds = new string[](7);
cmds[0] = "node";
cmds[1] = "dist/scripts/differential-testing.js";
cmds[2] = "hashOutputRootProof";
cmds[3] = Strings.toHexString(uint256(_version));
cmds[4] = Strings.toHexString(uint256(_stateRoot));
cmds[5] = Strings.toHexString(uint256(_messagePasserStorageRoot));
cmds[6] = Strings.toHexString(uint256(_latestBlockhash));
string[] memory cmds = new string[](6);
cmds[0] = "scripts/differential-testing/differential-testing";
cmds[1] = "hashOutputRootProof";
cmds[2] = Strings.toHexString(uint256(_version));
cmds[3] = Strings.toHexString(uint256(_stateRoot));
cmds[4] = Strings.toHexString(uint256(_messagePasserStorageRoot));
cmds[5] = Strings.toHexString(uint256(_latestBlockhash));
bytes memory result = vm.ffi(cmds);
return abi.decode(result, (bytes32));
......@@ -572,20 +569,19 @@ contract FFIInterface is Test {
uint256 _value,
uint64 _gas,
bytes memory _data,
uint256 _logIndex
uint64 _logIndex
) external returns (bytes32) {
string[] memory cmds = new string[](11);
cmds[0] = "node";
cmds[1] = "dist/scripts/differential-testing.js";
cmds[2] = "hashDepositTransaction";
cmds[3] = "0x0000000000000000000000000000000000000000000000000000000000000000";
cmds[4] = vm.toString(_logIndex);
cmds[5] = vm.toString(_from);
cmds[6] = vm.toString(_to);
cmds[7] = vm.toString(_mint);
cmds[8] = vm.toString(_value);
cmds[9] = vm.toString(_gas);
cmds[10] = vm.toString(_data);
string[] memory cmds = new string[](10);
cmds[0] = "scripts/differential-testing/differential-testing";
cmds[1] = "hashDepositTransaction";
cmds[2] = "0x0000000000000000000000000000000000000000000000000000000000000000";
cmds[3] = vm.toString(_logIndex);
cmds[4] = vm.toString(_from);
cmds[5] = vm.toString(_to);
cmds[6] = vm.toString(_mint);
cmds[7] = vm.toString(_value);
cmds[8] = vm.toString(_gas);
cmds[9] = vm.toString(_data);
bytes memory result = vm.ffi(cmds);
return abi.decode(result, (bytes32));
......@@ -595,19 +591,18 @@ contract FFIInterface is Test {
external
returns (bytes memory)
{
string[] memory cmds = new string[](12);
cmds[0] = "node";
cmds[1] = "dist/scripts/differential-testing.js";
cmds[2] = "encodeDepositTransaction";
cmds[3] = vm.toString(txn.from);
cmds[4] = vm.toString(txn.to);
cmds[5] = vm.toString(txn.value);
cmds[6] = vm.toString(txn.mint);
cmds[7] = vm.toString(txn.gasLimit);
cmds[8] = vm.toString(txn.isCreation);
cmds[9] = vm.toString(txn.data);
cmds[10] = vm.toString(txn.l1BlockHash);
cmds[11] = vm.toString(txn.logIndex);
string[] memory cmds = new string[](11);
cmds[0] = "scripts/differential-testing/differential-testing";
cmds[1] = "encodeDepositTransaction";
cmds[2] = vm.toString(txn.from);
cmds[3] = vm.toString(txn.to);
cmds[4] = vm.toString(txn.value);
cmds[5] = vm.toString(txn.mint);
cmds[6] = vm.toString(txn.gasLimit);
cmds[7] = vm.toString(txn.isCreation);
cmds[8] = vm.toString(txn.data);
cmds[9] = vm.toString(txn.l1BlockHash);
cmds[10] = vm.toString(txn.logIndex);
bytes memory result = vm.ffi(cmds);
return abi.decode(result, (bytes));
......@@ -621,27 +616,25 @@ contract FFIInterface is Test {
uint256 _gasLimit,
bytes memory _data
) external returns (bytes memory) {
string[] memory cmds = new string[](9);
cmds[0] = "node";
cmds[1] = "dist/scripts/differential-testing.js";
cmds[2] = "encodeCrossDomainMessage";
cmds[3] = vm.toString(_nonce);
cmds[4] = vm.toString(_sender);
cmds[5] = vm.toString(_target);
cmds[6] = vm.toString(_value);
cmds[7] = vm.toString(_gasLimit);
cmds[8] = vm.toString(_data);
string[] memory cmds = new string[](8);
cmds[0] = "scripts/differential-testing/differential-testing";
cmds[1] = "encodeCrossDomainMessage";
cmds[2] = vm.toString(_nonce);
cmds[3] = vm.toString(_sender);
cmds[4] = vm.toString(_target);
cmds[5] = vm.toString(_value);
cmds[6] = vm.toString(_gasLimit);
cmds[7] = vm.toString(_data);
bytes memory result = vm.ffi(cmds);
return abi.decode(result, (bytes));
}
function decodeVersionedNonce(uint256 nonce) external returns (uint256, uint256) {
string[] memory cmds = new string[](4);
cmds[0] = "node";
cmds[1] = "dist/scripts/differential-testing.js";
cmds[2] = "decodeVersionedNonce";
cmds[3] = vm.toString(nonce);
string[] memory cmds = new string[](3);
cmds[0] = "scripts/differential-testing/differential-testing";
cmds[1] = "decodeVersionedNonce";
cmds[2] = vm.toString(nonce);
bytes memory result = vm.ffi(cmds);
return abi.decode(result, (uint256, uint256));
......
......@@ -91,7 +91,7 @@ contract Encoding_Test is CommonTest {
uint64 _gas,
bool isCreate,
bytes memory _data,
uint256 _logIndex
uint64 _logIndex
) external {
Types.UserDepositTransaction memory t = Types.UserDepositTransaction(
_from,
......
......@@ -129,7 +129,7 @@ contract Hashing_hashDepositTransaction_Test is CommonTest {
uint256 _value,
uint64 _gas,
bytes memory _data,
uint256 _logIndex
uint64 _logIndex
) external {
assertEq(
Hashing.hashDepositTransaction(
......
......@@ -17,7 +17,7 @@
"bindings": "cd ../../op-bindings && make",
"build:forge": "forge build",
"build:with-metadata": "FOUNDRY_PROFILE=echidna yarn build:forge",
"build:differential": "tsc scripts/differential-testing.ts --outDir dist --moduleResolution node --esModuleInterop",
"build:differential": "tsc scripts/differential-testing.ts --outDir dist --moduleResolution node --esModuleInterop && go build -o ./scripts/differential-testing/differential-testing ./scripts/differential-testing",
"build:fuzz": "(cd test-case-generator && go build ./cmd/fuzz.go)",
"prebuild": "yarn ts-node scripts/verify-foundry-install.ts",
"build": "hardhat compile && yarn autogen:artifacts && yarn build:ts && yarn typechain",
......
package main
import (
"fmt"
"math/big"
"os"
"github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
)
// ABI types
var (
// Plain dynamic bytes type
bytes, _ = abi.NewType("bytes", "bytes", []abi.ArgumentMarshaling{
{Name: "data", Type: "bytes"},
})
bytesArgs = abi.Arguments{
{Type: bytes},
}
// Plain fixed bytes32 type
fixedBytes, _ = abi.NewType("bytes32", "bytes32", []abi.ArgumentMarshaling{
{Name: "data", Type: "bytes32"},
})
fixedBytesArgs = abi.Arguments{
{Type: fixedBytes},
}
// Decoded nonce tuple (nonce, version)
decodedNonce, _ = abi.NewType("tuple", "DecodedNonce", []abi.ArgumentMarshaling{
{Name: "nonce", Type: "uint256"},
{Name: "version", Type: "uint256"},
})
decodedNonceArgs = abi.Arguments{
{Name: "nonce", Type: decodedNonce},
}
// Withdrawal transaction tuple (uint256, address, address, uint256, uint256, bytes)
withdrawalTransaction, _ = abi.NewType("tuple", "WithdrawalTransaction", []abi.ArgumentMarshaling{
{Name: "nonce", Type: "uint256"},
{Name: "sender", Type: "address"},
{Name: "target", Type: "address"},
{Name: "value", Type: "uint256"},
{Name: "gasLimit", Type: "uint256"},
{Name: "data", Type: "bytes"},
})
withdrawalTransactionArgs = abi.Arguments{
{Name: "withdrawalTx", Type: withdrawalTransaction},
}
// Output root proof tuple (bytes32, bytes32, bytes32, bytes32)
outputRootProof, _ = abi.NewType("tuple", "OutputRootProof", []abi.ArgumentMarshaling{
{Name: "version", Type: "bytes32"},
{Name: "stateRoot", Type: "bytes32"},
{Name: "messagePasserStorageRoot", Type: "bytes32"},
{Name: "latestBlockHash", Type: "bytes32"},
})
outputRootProofArgs = abi.Arguments{
{Name: "proof", Type: outputRootProof},
}
)
func main() {
args := os.Args[1:]
// This command requires arguments
if len(args) == 0 {
panic("Error: No arguments provided")
}
switch args[0] {
case "decodeVersionedNonce":
input, ok := new(big.Int).SetString(args[1], 10)
checkOk(ok)
// Decode versioned nonce
nonce, version := crossdomain.DecodeVersionedNonce(input)
// ABI encode output
packArgs := struct {
Nonce *big.Int
Version *big.Int
}{
nonce,
version,
}
packed, err := decodedNonceArgs.Pack(&packArgs)
checkErr(err, fmt.Sprintf("Error encoding output: %s", err))
fmt.Print(hexutil.Encode(packed))
break
case "encodeCrossDomainMessage":
nonce, ok := new(big.Int).SetString(args[1], 10)
checkOk(ok)
sender := common.HexToAddress(args[2])
target := common.HexToAddress(args[3])
value, ok := new(big.Int).SetString(args[4], 10)
checkOk(ok)
gasLimit, ok := new(big.Int).SetString(args[5], 10)
checkOk(ok)
data := common.Hex2Bytes(args[6][2:])
// Encode cross domain message
encoded, err := encodeCrossDomainMessage(nonce, sender, target, value, gasLimit, data)
checkErr(err, fmt.Sprintf("Error encoding cross domain message: %s", err))
// Pack encoded cross domain message
packed, err := bytesArgs.Pack(&encoded)
checkErr(err, fmt.Sprintf("Error encoding output: %s", err))
fmt.Print(hexutil.Encode(packed))
break
case "hashCrossDomainMessage":
// Parse input arguments
nonce, ok := new(big.Int).SetString(args[1], 10)
checkOk(ok)
sender := common.HexToAddress(args[2])
target := common.HexToAddress(args[3])
value, ok := new(big.Int).SetString(args[4], 10)
checkOk(ok)
gasLimit, ok := new(big.Int).SetString(args[5], 10)
checkOk(ok)
data := common.Hex2Bytes(args[6][2:])
// Encode cross domain message
encoded, err := encodeCrossDomainMessage(nonce, sender, target, value, gasLimit, data)
checkErr(err, fmt.Sprintf("Error encoding cross domain message: %s", err))
// Hash encoded cross domain message
hash := crypto.Keccak256Hash(encoded)
// Pack hash
packed, err := fixedBytesArgs.Pack(&hash)
checkErr(err, fmt.Sprintf("Error encoding output: %s", err))
fmt.Print(hexutil.Encode(packed))
break
case "hashDepositTransaction":
// Parse input arguments
l1BlockHash := common.HexToHash(args[1])
logIndex, ok := new(big.Int).SetString(args[2], 10)
checkOk(ok)
from := common.HexToAddress(args[3])
to := common.HexToAddress(args[4])
mint, ok := new(big.Int).SetString(args[5], 10)
checkOk(ok)
value, ok := new(big.Int).SetString(args[6], 10)
checkOk(ok)
gasLimit, ok := new(big.Int).SetString(args[7], 10)
checkOk(ok)
data := common.FromHex(args[8])
// Create deposit transaction
depositTx := makeDepositTx(from, to, value, mint, gasLimit, false, data, l1BlockHash, logIndex)
// RLP encode deposit transaction
encoded, err := types.NewTx(&depositTx).MarshalBinary()
checkErr(err, fmt.Sprintf("Error encoding deposit transaction: %s", err))
// Hash encoded deposit transaction
hash := crypto.Keccak256Hash(encoded)
// Pack hash
packed, err := fixedBytesArgs.Pack(&hash)
checkErr(err, fmt.Sprintf("Error encoding output: %s", err))
fmt.Print(hexutil.Encode(packed))
break
case "encodeDepositTransaction":
// Parse input arguments
from := common.HexToAddress(args[1])
to := common.HexToAddress(args[2])
value, ok := new(big.Int).SetString(args[3], 10)
checkOk(ok)
mint, ok := new(big.Int).SetString(args[4], 10)
checkOk(ok)
gasLimit, ok := new(big.Int).SetString(args[5], 10)
checkOk(ok)
isCreate := args[6] == "true"
data := common.FromHex(args[7])
l1BlockHash := common.HexToHash(args[8])
logIndex, ok := new(big.Int).SetString(args[9], 10)
checkOk(ok)
depositTx := makeDepositTx(from, to, value, mint, gasLimit, isCreate, data, l1BlockHash, logIndex)
// RLP encode deposit transaction
encoded, err := types.NewTx(&depositTx).MarshalBinary()
checkErr(err, fmt.Sprintf("Failed to RLP encode deposit transaction: %s", err))
// Pack rlp encoded deposit transaction
packed, err := bytesArgs.Pack(&encoded)
checkErr(err, fmt.Sprintf("Error encoding output: %s", err))
fmt.Print(hexutil.Encode(packed))
break
case "hashWithdrawal":
// Parse input arguments
nonce, ok := new(big.Int).SetString(args[1], 10)
checkOk(ok)
sender := common.HexToAddress(args[2])
target := common.HexToAddress(args[3])
value, ok := new(big.Int).SetString(args[4], 10)
checkOk(ok)
gasLimit, ok := new(big.Int).SetString(args[5], 10)
checkOk(ok)
data := common.FromHex(args[6])
// Pack withdrawal
wdtx := struct {
Nonce *big.Int
Sender common.Address
Target common.Address
Value *big.Int
GasLimit *big.Int
Data []byte
}{
Nonce: nonce,
Sender: sender,
Target: target,
Value: value,
GasLimit: gasLimit,
Data: data,
}
packed, err := withdrawalTransactionArgs.Pack(&wdtx)
// println(hexutil.Encode(packed))
checkErr(err, fmt.Sprintf("Error packing withdrawal: %s", err))
// Hash packed withdrawal (we ignore the pointer)
hash := crypto.Keccak256Hash(packed[32:])
// Pack hash
packed, err = fixedBytesArgs.Pack(&hash)
checkErr(err, fmt.Sprintf("Error encoding output: %s", err))
fmt.Print(hexutil.Encode(packed))
break
case "hashOutputRootProof":
// Parse input arguments
version := common.HexToHash(args[1])
stateRoot := common.HexToHash(args[2])
messagePasserStorageRoot := common.HexToHash(args[3])
latestBlockHash := common.HexToHash(args[4])
// Pack proof
proof := struct {
Version common.Hash
StateRoot common.Hash
MessagePasserStorageRoot common.Hash
LatestBlockHash common.Hash
}{
Version: version,
StateRoot: stateRoot,
MessagePasserStorageRoot: messagePasserStorageRoot,
LatestBlockHash: latestBlockHash,
}
packed, err := outputRootProofArgs.Pack(&proof)
checkErr(err, fmt.Sprintf("Error packing proof: %s", err))
// Hash packed proof
hash := crypto.Keccak256Hash(packed)
// Pack hash
packed, err = fixedBytesArgs.Pack(&hash)
checkErr(err, fmt.Sprintf("Error encoding output: %s", err))
fmt.Print(hexutil.Encode(packed))
break
case "getProveWithdrawalTransactionInputs":
// TODO
break
default:
panic(fmt.Errorf("Unknown command: %s", args[0]))
}
}
package main
import (
"errors"
"fmt"
"math/big"
"github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
var UnknownNonceVersion = errors.New("Unknown nonce version")
// checkOk checks if ok is false, and panics if so.
// Shorthand to ease go's god awful error handling
func checkOk(ok bool) {
if !ok {
panic(fmt.Errorf("checkOk failed"))
}
}
// checkErr checks if err is not nil, and throws if so.
// Shorthand to ease go's god awful error handling
func checkErr(err error, failReason string) {
if err != nil {
panic(fmt.Errorf("%s: %s", failReason, err))
}
}
// encodeCrossDomainMessage encodes a versioned cross domain message into a byte array.
func encodeCrossDomainMessage(nonce *big.Int, sender common.Address, target common.Address, value *big.Int, gasLimit *big.Int, data []byte) ([]byte, error) {
_, version := crossdomain.DecodeVersionedNonce(nonce)
var encoded []byte
var err error
if version.Cmp(big.NewInt(0)) == 0 {
// Encode cross domain message V0
encoded, err = crossdomain.EncodeCrossDomainMessageV0(target, sender, data, nonce)
} else if version.Cmp(big.NewInt(1)) == 0 {
// Encode cross domain message V1
encoded, err = crossdomain.EncodeCrossDomainMessageV1(nonce, sender, target, value, gasLimit, data)
} else {
return nil, UnknownNonceVersion
}
return encoded, err
}
// makeDepositTx creates a deposit transaction type.
func makeDepositTx(
from common.Address,
to common.Address,
value *big.Int,
mint *big.Int,
gasLimit *big.Int,
isCreate bool,
data []byte,
l1BlockHash common.Hash,
logIndex *big.Int,
) types.DepositTx {
// Create deposit transaction source
udp := derive.UserDepositSource{
L1BlockHash: l1BlockHash,
LogIndex: logIndex.Uint64(),
}
// Create deposit transaction
depositTx := types.DepositTx{
SourceHash: udp.SourceHash(),
From: from,
Value: value,
Gas: gasLimit.Uint64(),
IsSystemTransaction: false, // This will never be a system transaction in the tests.
Data: data,
}
// Fill optional fields
if mint.Cmp(big.NewInt(0)) == 1 {
depositTx.Mint = mint
}
if !isCreate {
depositTx.To = &to
}
return depositTx
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment