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 77 78 79 80 81 82 83 84 85 86
)

func main() {
	args := os.Args[1:]

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

	switch args[0] {
	case "decodeVersionedNonce":
87
		// Parse input arguments
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
		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)
103
		checkErr(err, "Error encoding output")
104 105 106

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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