differential-testing.go 11 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 22 23
)

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

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

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

	// 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},
	}
74 75 76 77 78 79 80 81 82 83 84 85
)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

272 273 274
		// Create a secure trie for state
		state, err := trie.NewStateTrie(
			trie.TrieID(types.EmptyRootHash),
275 276
			trie.NewDatabase(rawdb.NewMemoryDatabase()),
		)
277
		checkErr(err, "Error creating secure trie")
278 279

		// Put a "true" bool in the storage slot
280
		state.UpdateStorage(common.Address{}, hash.Bytes(), []byte{0x01})
281 282 283

		// Create a secure trie for the world state
		world, err := trie.NewStateTrie(
284
			trie.TrieID(types.EmptyRootHash),
285 286
			trie.NewDatabase(rawdb.NewMemoryDatabase()),
		)
287
		checkErr(err, "Error creating secure trie")
288

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

		// Get the proof
		var proof proofList
301
		checkErr(state.Prove(predeploys.L2ToL1MessagePasserAddr.Bytes(), 0, &proof), "Error getting proof")
302 303

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

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

		// Print the output
		fmt.Print(hexutil.Encode(packed[32:]))
326 327 328 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
	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:]))
359 360 361 362
	default:
		panic(fmt.Errorf("Unknown command: %s", args[0]))
	}
}