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

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

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

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

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

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

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

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

	switch args[0] {
	case "decodeVersionedNonce":
75
		// Parse input arguments
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
		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)
91
		checkErr(err, "Error encoding output")
92 93 94

		fmt.Print(hexutil.Encode(packed))
	case "encodeCrossDomainMessage":
95
		// Parse input arguments
96 97 98 99 100 101 102 103
		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
104
		data := common.FromHex(args[6])
105 106 107

		// Encode cross domain message
		encoded, err := encodeCrossDomainMessage(nonce, sender, target, value, gasLimit, data)
108
		checkErr(err, "Error encoding cross domain message")
109 110 111

		// Pack encoded cross domain message
		packed, err := bytesArgs.Pack(&encoded)
112
		checkErr(err, "Error encoding output")
113 114 115 116 117 118 119 120 121 122 123 124

		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
125
		data := common.FromHex(args[6])
126 127 128

		// Encode cross domain message
		encoded, err := encodeCrossDomainMessage(nonce, sender, target, value, gasLimit, data)
129
		checkErr(err, "Error encoding cross domain message")
130 131 132 133 134 135

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

		// Pack hash
		packed, err := fixedBytesArgs.Pack(&hash)
136
		checkErr(err, "Error encoding output")
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158

		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()
159
		checkErr(err, "Error encoding deposit transaction")
160 161 162 163 164 165

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

		// Pack hash
		packed, err := fixedBytesArgs.Pack(&hash)
166
		checkErr(err, "Error encoding output")
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188

		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()
189
		checkErr(err, "Failed to RLP encode deposit transaction")
190 191
		// Pack rlp encoded deposit transaction
		packed, err := bytesArgs.Pack(&encoded)
192
		checkErr(err, "Error encoding output")
193 194 195 196 197 198 199 200 201 202 203 204 205 206

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

207 208
		// Hash withdrawal
		hash, err := hashWithdrawal(nonce, sender, target, value, gasLimit, data)
209
		checkErr(err, "Error hashing withdrawal")
210 211

		// Pack hash
212
		packed, err := fixedBytesArgs.Pack(&hash)
213
		checkErr(err, "Error encoding output")
214 215 216 217 218 219 220 221 222

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

223
		// Hash the output root proof
224 225
		hash, err := hashOutputRootProof(version, stateRoot, messagePasserStorageRoot, latestBlockHash)
		checkErr(err, "Error hashing output root proof")
226 227

		// Pack hash
228
		packed, err := fixedBytesArgs.Pack(&hash)
229
		checkErr(err, "Error encoding output")
230 231 232

		fmt.Print(hexutil.Encode(packed))
	case "getProveWithdrawalTransactionInputs":
233 234 235 236 237 238 239 240 241 242 243 244
		// 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)
245
		checkErr(err, "Error hashing withdrawal")
246 247 248 249 250 251 252 253 254 255

		// 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)
256
		checkErr(err, "Error packing withdrawal slot")
257 258 259 260

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

261 262 263
		// Create a secure trie for state
		state, err := trie.NewStateTrie(
			trie.TrieID(types.EmptyRootHash),
264 265
			trie.NewDatabase(rawdb.NewMemoryDatabase()),
		)
266
		checkErr(err, "Error creating secure trie")
267 268

		// Put a "true" bool in the storage slot
269
		state.UpdateStorage(common.Address{}, hash.Bytes(), []byte{0x01})
270 271 272

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

278
		// Put the put the rlp encoded account in the world trie
279 280 281
		account := types.StateAccount{
			Nonce:   0,
			Balance: big.NewInt(0),
282
			Root:    state.Hash(),
283 284
		}
		writer := new(bytes.Buffer)
285
		checkErr(account.EncodeRLP(writer), "Error encoding account")
286
		world.UpdateStorage(common.Address{}, predeploys.L2ToL1MessagePasserAddr.Bytes(), writer.Bytes())
287 288 289

		// Get the proof
		var proof proofList
290
		checkErr(state.Prove(predeploys.L2ToL1MessagePasserAddr.Bytes(), 0, &proof), "Error getting proof")
291 292

		// Get the output root
293 294
		outputRoot, err := hashOutputRootProof(common.Hash{}, world.Hash(), state.Hash(), common.Hash{})
		checkErr(err, "Error hashing output root proof")
295 296 297 298

		// Pack the output
		output := struct {
			WorldRoot      common.Hash
299
			StateRoot      common.Hash
300 301
			OutputRoot     common.Hash
			WithdrawalHash common.Hash
302
			Proof          proofList
303 304
		}{
			WorldRoot:      world.Hash(),
305
			StateRoot:      state.Hash(),
306 307 308 309 310
			OutputRoot:     outputRoot,
			WithdrawalHash: wdHash,
			Proof:          proof,
		}
		packed, err = proveWithdrawalInputsArgs.Pack(&output)
311
		checkErr(err, "Error encoding output")
312 313 314

		// Print the output
		fmt.Print(hexutil.Encode(packed[32:]))
315 316 317 318
	default:
		panic(fmt.Errorf("Unknown command: %s", args[0]))
	}
}