differential-testing.go 11.1 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 281
		err = state.UpdateStorage(common.Address{}, hash.Bytes(), []byte{0x01})
		checkErr(err, "Error updating storage")
282 283 284

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

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

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

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

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

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