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
	"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
21 22 23 24
)

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

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

46
	// WithdrawalHash slot tuple (bytes32, bytes32)
47
	withdrawalSlot, _ = abi.NewType("tuple", "SlotHash", []abi.ArgumentMarshaling{
48 49 50 51 52 53 54 55 56 57
		{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"},
58
		{Name: "stateRoot", Type: "bytes32"},
59 60 61 62 63 64 65
		{Name: "outputRoot", Type: "bytes32"},
		{Name: "withdrawalHash", Type: "bytes32"},
		{Name: "proof", Type: "bytes[]"},
	})
	proveWithdrawalInputsArgs = abi.Arguments{
		{Name: "inputs", Type: proveWithdrawalInputs},
	}
66 67 68 69 70 71 72 73 74

	// 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},
	}
75 76
)

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

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

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

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

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

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

		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
138
		data := common.FromHex(args[6])
139 140 141

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

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

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

		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()
172
		checkErr(err, "Error encoding deposit transaction")
173 174 175 176 177 178

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

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

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

		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])

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

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

		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])

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

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

		fmt.Print(hexutil.Encode(packed))
	case "getProveWithdrawalTransactionInputs":
246 247 248 249 250 251 252 253 254 255 256 257
		// 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)
258
		checkErr(err, "Error hashing withdrawal")
259 260 261 262 263 264 265 266 267 268

		// 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)
269
		checkErr(err, "Error packing withdrawal slot")
270 271 272 273

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

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

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

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

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

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

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

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

		// Print the output
		fmt.Print(hexutil.Encode(packed[32:]))
330 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
	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:]))
363 364 365 366
	default:
		panic(fmt.Errorf("Unknown command: %s", args[0]))
	}
}