differential-testing.go 11.3 KB
Newer Older
1 2 3
package main

import (
4
	"bytes"
5 6 7
	"fmt"
	"math/big"
	"os"
8
	"strconv"
9

10
	"github.com/ethereum-optimism/optimism/cannon/mipsevm"
11
	"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
12 13 14 15
	"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"
16
	"github.com/ethereum/go-ethereum/core/rawdb"
17 18
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/crypto"
19
	"github.com/ethereum/go-ethereum/trie"
20 21
	"github.com/ethereum/go-ethereum/triedb"
	"github.com/ethereum/go-ethereum/triedb/hashdb"
22 23 24 25
)

// ABI types
var (
26 27 28 29
	// Plain dynamic dynBytes type
	dynBytes, _ = abi.NewType("bytes", "", nil)
	bytesArgs   = abi.Arguments{
		{Type: dynBytes},
30 31 32
	}

	// Plain fixed bytes32 type
33
	fixedBytes, _  = abi.NewType("bytes32", "", nil)
34 35 36 37 38 39 40 41 42 43
	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{
44
		{Name: "encodedNonce", Type: decodedNonce},
45 46
	}

47
	// WithdrawalHash slot tuple (bytes32, bytes32)
48
	withdrawalSlot, _ = abi.NewType("tuple", "SlotHash", []abi.ArgumentMarshaling{
49 50 51 52 53 54 55 56 57 58
		{Name: "withdrawalHash", Type: "bytes32"},
		{Name: "zeroPadding", Type: "bytes32"},
	})
	withdrawalSlotArgs = abi.Arguments{
		{Name: "slotHash", Type: withdrawalSlot},
	}

	// Prove withdrawal inputs tuple (bytes32, bytes32, bytes32, bytes32, bytes[])
	proveWithdrawalInputs, _ = abi.NewType("tuple", "ProveWithdrawalInputs", []abi.ArgumentMarshaling{
		{Name: "worldRoot", Type: "bytes32"},
59
		{Name: "stateRoot", Type: "bytes32"},
60 61 62 63 64 65 66
		{Name: "outputRoot", Type: "bytes32"},
		{Name: "withdrawalHash", Type: "bytes32"},
		{Name: "proof", Type: "bytes[]"},
	})
	proveWithdrawalInputsArgs = abi.Arguments{
		{Name: "inputs", Type: proveWithdrawalInputs},
	}
67 68 69 70 71 72 73 74 75

	// cannonMemoryProof inputs tuple (bytes32, bytes)
	cannonMemoryProof, _ = abi.NewType("tuple", "CannonMemoryProof", []abi.ArgumentMarshaling{
		{Name: "memRoot", Type: "bytes32"},
		{Name: "proof", Type: "bytes"},
	})
	cannonMemoryProofArgs = abi.Arguments{
		{Name: "encodedCannonMemoryProof", Type: cannonMemoryProof},
	}
76 77
)

clabby's avatar
clabby committed
78 79
func DiffTestUtils() {
	args := os.Args[2:]
clabby's avatar
clabby committed
80
	variant := args[0]
81 82 83 84 85 86

	// This command requires arguments
	if len(args) == 0 {
		panic("Error: No arguments provided")
	}

clabby's avatar
clabby committed
87
	switch variant {
88
	case "decodeVersionedNonce":
89
		// Parse input arguments
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
		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)
105
		checkErr(err, "Error encoding output")
106 107 108

		fmt.Print(hexutil.Encode(packed))
	case "encodeCrossDomainMessage":
109
		// Parse input arguments
110 111 112 113 114 115 116 117
		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)
clabby's avatar
clabby committed
118
		data := common.FromHex(args[6])
119 120 121

		// Encode cross domain message
		encoded, err := encodeCrossDomainMessage(nonce, sender, target, value, gasLimit, data)
122
		checkErr(err, "Error encoding cross domain message")
123 124 125

		// Pack encoded cross domain message
		packed, err := bytesArgs.Pack(&encoded)
126
		checkErr(err, "Error encoding output")
127 128 129 130 131 132 133 134 135 136 137 138

		fmt.Print(hexutil.Encode(packed))
	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)
clabby's avatar
clabby committed
139
		data := common.FromHex(args[6])
140 141 142

		// Encode cross domain message
		encoded, err := encodeCrossDomainMessage(nonce, sender, target, value, gasLimit, data)
143
		checkErr(err, "Error encoding cross domain message")
144 145 146 147 148 149

		// Hash encoded cross domain message
		hash := crypto.Keccak256Hash(encoded)

		// Pack hash
		packed, err := fixedBytesArgs.Pack(&hash)
150
		checkErr(err, "Error encoding output")
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172

		fmt.Print(hexutil.Encode(packed))
	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()
173
		checkErr(err, "Error encoding deposit transaction")
174 175 176 177 178 179

		// Hash encoded deposit transaction
		hash := crypto.Keccak256Hash(encoded)

		// Pack hash
		packed, err := fixedBytesArgs.Pack(&hash)
180
		checkErr(err, "Error encoding output")
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202

		fmt.Print(hexutil.Encode(packed))
	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()
203
		checkErr(err, "Failed to RLP encode deposit transaction")
204 205
		// Pack rlp encoded deposit transaction
		packed, err := bytesArgs.Pack(&encoded)
206
		checkErr(err, "Error encoding output")
207 208 209 210 211 212 213 214 215 216 217 218 219 220

		fmt.Print(hexutil.Encode(packed))
	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])

221 222
		// Hash withdrawal
		hash, err := hashWithdrawal(nonce, sender, target, value, gasLimit, data)
223
		checkErr(err, "Error hashing withdrawal")
224 225

		// Pack hash
226
		packed, err := fixedBytesArgs.Pack(&hash)
227
		checkErr(err, "Error encoding output")
228 229 230 231 232 233 234 235 236

		fmt.Print(hexutil.Encode(packed))
	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])

237
		// Hash the output root proof
238 239
		hash, err := hashOutputRootProof(version, stateRoot, messagePasserStorageRoot, latestBlockHash)
		checkErr(err, "Error hashing output root proof")
240 241

		// Pack hash
242
		packed, err := fixedBytesArgs.Pack(&hash)
243
		checkErr(err, "Error encoding output")
244 245 246

		fmt.Print(hexutil.Encode(packed))
	case "getProveWithdrawalTransactionInputs":
247 248 249 250 251 252 253 254 255 256 257 258
		// 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])

		wdHash, err := hashWithdrawal(nonce, sender, target, value, gasLimit, data)
259
		checkErr(err, "Error hashing withdrawal")
260 261 262 263 264 265 266 267 268 269

		// Compute the storage slot the withdrawalHash will be stored in
		slot := struct {
			WithdrawalHash common.Hash
			ZeroPadding    common.Hash
		}{
			WithdrawalHash: wdHash,
			ZeroPadding:    common.Hash{},
		}
		packed, err := withdrawalSlotArgs.Pack(&slot)
270
		checkErr(err, "Error packing withdrawal slot")
271 272 273 274

		// Compute the storage slot the withdrawalHash will be stored in
		hash := crypto.Keccak256Hash(packed)

275 276 277
		// Create a secure trie for state
		state, err := trie.NewStateTrie(
			trie.TrieID(types.EmptyRootHash),
278
			triedb.NewDatabase(rawdb.NewMemoryDatabase(), &triedb.Config{HashDB: hashdb.Defaults}),
279
		)
280
		checkErr(err, "Error creating secure trie")
281 282

		// Put a "true" bool in the storage slot
283 284
		err = state.UpdateStorage(common.Address{}, hash.Bytes(), []byte{0x01})
		checkErr(err, "Error updating storage")
285 286 287

		// Create a secure trie for the world state
		world, err := trie.NewStateTrie(
288
			trie.TrieID(types.EmptyRootHash),
289
			triedb.NewDatabase(rawdb.NewMemoryDatabase(), &triedb.Config{HashDB: hashdb.Defaults}),
290
		)
291
		checkErr(err, "Error creating secure trie")
292

293
		// Put the put the rlp encoded account in the world trie
294 295
		account := types.StateAccount{
			Nonce:   0,
296
			Balance: common.U2560,
297
			Root:    state.Hash(),
298 299
		}
		writer := new(bytes.Buffer)
300
		checkErr(account.EncodeRLP(writer), "Error encoding account")
301 302
		err = world.UpdateStorage(common.Address{}, predeploys.L2ToL1MessagePasserAddr.Bytes(), writer.Bytes())
		checkErr(err, "Error updating storage")
303 304 305

		// Get the proof
		var proof proofList
306
		checkErr(state.Prove(predeploys.L2ToL1MessagePasserAddr.Bytes(), &proof), "Error getting proof")
307 308

		// Get the output root
309 310
		outputRoot, err := hashOutputRootProof(common.Hash{}, world.Hash(), state.Hash(), common.Hash{})
		checkErr(err, "Error hashing output root proof")
311 312 313 314

		// Pack the output
		output := struct {
			WorldRoot      common.Hash
315
			StateRoot      common.Hash
316 317
			OutputRoot     common.Hash
			WithdrawalHash common.Hash
318
			Proof          proofList
319 320
		}{
			WorldRoot:      world.Hash(),
321
			StateRoot:      state.Hash(),
322 323 324 325 326
			OutputRoot:     outputRoot,
			WithdrawalHash: wdHash,
			Proof:          proof,
		}
		packed, err = proveWithdrawalInputsArgs.Pack(&output)
327
		checkErr(err, "Error encoding output")
328 329 330

		// Print the output
		fmt.Print(hexutil.Encode(packed[32:]))
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
	case "cannonMemoryProof":
		// <pc, insn, [memAddr, memValue]>
		mem := mipsevm.NewMemory()
		if len(args) != 3 && len(args) != 5 {
			panic("Error: cannonMemoryProofWithProof requires 2 or 4 arguments")
		}
		pc, err := strconv.ParseUint(args[1], 10, 32)
		checkErr(err, "Error decocding addr")
		insn, err := strconv.ParseUint(args[2], 10, 32)
		checkErr(err, "Error decocding insn")
		mem.SetMemory(uint32(pc), uint32(insn))

		var insnProof, memProof [896]byte
		if len(args) == 5 {
			memAddr, err := strconv.ParseUint(args[3], 10, 32)
			checkErr(err, "Error decocding memAddr")
			memValue, err := strconv.ParseUint(args[4], 10, 32)
			checkErr(err, "Error decocding memValue")
			mem.SetMemory(uint32(memAddr), uint32(memValue))
			memProof = mem.MerkleProof(uint32(memAddr))
		}
		insnProof = mem.MerkleProof(uint32(pc))

		output := struct {
			MemRoot common.Hash
			Proof   []byte
		}{
			MemRoot: mem.MerkleRoot(),
			Proof:   append(insnProof[:], memProof[:]...),
		}
		packed, err := cannonMemoryProofArgs.Pack(&output)
		checkErr(err, "Error encoding output")
		fmt.Print(hexutil.Encode(packed[32:]))
364 365 366 367
	default:
		panic(fmt.Errorf("Unknown command: %s", args[0]))
	}
}