validity_test.go 26 KB
Newer Older
1
package bridge
2 3 4 5 6 7 8 9 10 11 12

import (
	"bytes"
	"context"
	"crypto/ecdsa"
	"fmt"
	"math/big"
	"math/rand"
	"testing"
	"time"

13 14
	"github.com/ethereum-optimism/optimism/op-e2e/config"

15 16 17 18 19
	op_e2e "github.com/ethereum-optimism/optimism/op-e2e"

	"github.com/ethereum-optimism/optimism/op-e2e/system/e2esys"
	"github.com/ethereum-optimism/optimism/op-e2e/system/helpers"

20
	"github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain"
21
	legacybindings "github.com/ethereum-optimism/optimism/op-e2e/bindings"
22
	"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
23 24
	"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger"
	"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/disputegame"
25
	"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
26 27
	"github.com/ethereum-optimism/optimism/op-node/bindings"
	bindingspreview "github.com/ethereum-optimism/optimism/op-node/bindings/preview"
28
	"github.com/ethereum-optimism/optimism/op-service/predeploys"
Sabnock01's avatar
Sabnock01 committed
29
	"github.com/ethereum-optimism/optimism/op-service/testutils/fuzzerutils"
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
	"github.com/ethereum/go-ethereum/accounts"
	"github.com/ethereum/go-ethereum/accounts/abi/bind"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/ethclient"
	"github.com/ethereum/go-ethereum/ethclient/gethclient"
	"github.com/ethereum/go-ethereum/params"
	"github.com/ethereum/go-ethereum/rpc"
	fuzz "github.com/google/gofuzz"
	"github.com/stretchr/testify/require"
)

// TestAccount defines an account generated by startConfigWithTestAccounts
type TestAccount struct {
	HDPath string
	Key    *ecdsa.PrivateKey
	Addr   common.Address
	L1Opts *bind.TransactOpts
	L2Opts *bind.TransactOpts
}

// startConfigWithTestAccounts takes a SystemConfig, generates additional accounts, adds them to the config, so they
// are funded on startup, starts the system, and imports the keys into the keystore, and obtains transaction opts for
// each account.
55
func startConfigWithTestAccounts(t *testing.T, cfg *e2esys.SystemConfig, accountsToGenerate int) (*e2esys.System, []*TestAccount, error) {
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
	// Create our test accounts and add them to the pre-mine cfg.
	testAccounts := make([]*TestAccount, 0)
	var err error
	for i := 0; i < accountsToGenerate; i++ {
		// Create our test account and add it to our list
		testAccount := &TestAccount{
			HDPath: fmt.Sprintf("m/44'/60'/0'/0/%d", 1000+i), // offset by 1000 to avoid collisions.
			Key:    nil,
			L1Opts: nil,
			L2Opts: nil,
		}
		testAccounts = append(testAccounts, testAccount)

		// Obtain our generated private key
		testAccount.Key, err = cfg.Secrets.Wallet.PrivateKey(accounts.Account{
			URL: accounts.URL{
				Path: testAccount.HDPath,
			},
		})
		if err != nil {
			return nil, nil, err
		}
		testAccount.Addr = crypto.PubkeyToAddress(testAccount.Key.PublicKey)

		// Obtain the transaction options for contract bindings for this account.
		testAccount.L1Opts, err = bind.NewKeyedTransactorWithChainID(testAccount.Key, cfg.L1ChainIDBig())
		if err != nil {
			return nil, nil, err
		}
		testAccount.L2Opts, err = bind.NewKeyedTransactorWithChainID(testAccount.Key, cfg.L2ChainIDBig())
		if err != nil {
			return nil, nil, err
		}

		// Fund the test account in our config
		cfg.Premine[testAccount.Addr] = big.NewInt(params.Ether)
		cfg.Premine[testAccount.Addr] = cfg.Premine[testAccount.Addr].Mul(cfg.Premine[testAccount.Addr], big.NewInt(1_000_000))

	}

	// Start our system
97
	sys, err := cfg.Start(t)
98 99 100 101 102 103 104 105 106 107 108
	if err != nil {
		return sys, nil, err
	}

	// Return our results.
	return sys, testAccounts, err
}

// TestMixedDepositValidity makes a number of deposit transactions, some which will succeed in transferring value,
// while others do not. It ensures that the expected nonces/balances match after several interactions.
func TestMixedDepositValidity(t *testing.T) {
109
	op_e2e.InitParallel(t)
110 111 112 113 114 115 116 117
	// Define how many deposit txs we'll make. Each deposit mints a fixed amount and transfers up to 1/3 of the user's
	// balance. As such, this number cannot be too high or else the test will always fail due to lack of balance in L1.
	const depositTxCount = 15

	// Define how many accounts we'll use to deposit funds
	const accountUsedToDeposit = 5

	// Create our system configuration, funding all accounts we created for L1/L2, and start it
118
	cfg := e2esys.DefaultSystemConfig(t)
119
	sys, testAccounts, err := startConfigWithTestAccounts(t, &cfg, accountUsedToDeposit)
120 121 122
	require.Nil(t, err, "Error starting up system")

	// Obtain our sequencer, verifier, and transactor keypair.
123 124 125
	l1Client := sys.NodeClient("l1")
	l2Seq := sys.NodeClient("sequencer")
	l2Verif := sys.NodeClient("verifier")
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
	require.NoError(t, err)

	// Define our L1 transaction timeout duration.
	txTimeoutDuration := 10 * time.Duration(cfg.DeployConfig.L1BlockTime) * time.Second

	// Create a struct used to track our transactors and their transactions sent.
	type TestAccountState struct {
		Account           *TestAccount
		ExpectedL1Balance *big.Int
		ExpectedL2Balance *big.Int
		StartingL1Nonce   uint64
		ExpectedL1Nonce   uint64
		StartingL2Nonce   uint64
		ExpectedL2Nonce   uint64
	}

	// Create the state objects for every test account we'll track changes for.
	transactors := make([]*TestAccountState, 0)
	for i := 0; i < len(testAccounts); i++ {
		// Obtain our account
		testAccount := testAccounts[i]

		// Obtain the transactor's starting nonce on L1.
		ctx, cancel := context.WithTimeout(context.Background(), txTimeoutDuration)
		startL1Nonce, err := l1Client.NonceAt(ctx, testAccount.L1Opts.From, nil)
		cancel()
		require.NoError(t, err)

		// Obtain the transactor's starting balance on L2.
		ctx, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
		startL2Balance, err := l2Verif.BalanceAt(ctx, testAccount.L2Opts.From, nil)
		cancel()
		require.NoError(t, err)

		// Obtain the transactor's starting nonce on L2.
		ctx, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
		startL2Nonce, err := l2Verif.NonceAt(ctx, testAccount.L2Opts.From, nil)
		cancel()
		require.NoError(t, err)

		// Add our transactor to our list
		transactors = append(transactors, &TestAccountState{
			Account:           testAccount,
			ExpectedL2Balance: startL2Balance,
			ExpectedL1Nonce:   startL1Nonce,
			ExpectedL2Nonce:   startL2Nonce,
		})
	}

	// Create our random provider
176
	randomProvider := rand.New(rand.NewSource(1452))
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200

	// Now we create a number of deposits from each transactor
	for i := 0; i < depositTxCount; i++ {
		// Determine if this deposit should succeed in transferring value (not minting)
		validTransfer := randomProvider.Int()%2 == 0

		// Determine the transactor to use
		transactorIndex := randomProvider.Int() % len(transactors)
		transactor := transactors[transactorIndex]

		// Determine the transactor to receive the deposit
		receiverIndex := randomProvider.Int() % len(transactors)
		receiver := transactors[receiverIndex]
		toAddr := receiver.Account.L2Opts.From

		// Create our L1 deposit transaction and send it.
		mintAmount := big.NewInt(randomProvider.Int63() % 9_000_000)
		transactor.Account.L1Opts.Value = mintAmount
		var transferValue *big.Int
		if validTransfer {
			transferValue = new(big.Int).Div(transactor.ExpectedL2Balance, common.Big3) // send 1/3 our balance which should succeed.
		} else {
			transferValue = new(big.Int).Mul(common.Big2, transactor.ExpectedL2Balance) // trigger a revert by trying to transfer our current balance * 2
		}
201
		helpers.SendDepositTx(t, cfg, l1Client, l2Verif, transactor.Account.L1Opts, func(l2Opts *helpers.DepositTxOpts) {
202 203 204 205 206 207 208 209 210 211 212
			l2Opts.GasLimit = 100_000
			l2Opts.IsCreation = false
			l2Opts.Data = nil
			l2Opts.ToAddr = toAddr
			l2Opts.Value = transferValue
			if validTransfer {
				l2Opts.ExpectedStatus = types.ReceiptStatusSuccessful
			} else {
				l2Opts.ExpectedStatus = types.ReceiptStatusFailed
			}
		})
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268

		// Update our expected balances.
		if validTransfer && transactor != receiver {
			// Transactor balances changes by minted minus transferred value.
			transactor.ExpectedL2Balance = new(big.Int).Add(transactor.ExpectedL2Balance, new(big.Int).Sub(mintAmount, transferValue))
			// Receiver balance changes by transferred value.
			receiver.ExpectedL2Balance = new(big.Int).Add(receiver.ExpectedL2Balance, transferValue)
		} else {
			// If the transfer failed, minting should've still succeeded but the balance shouldn't have transferred
			// to the recipient.
			transactor.ExpectedL2Balance = new(big.Int).Add(transactor.ExpectedL2Balance, mintAmount)
		}
		transactor.ExpectedL1Nonce = transactor.ExpectedL1Nonce + 1
		transactor.ExpectedL2Nonce = transactor.ExpectedL2Nonce + 1
	}

	// At the end, assert our account balance/nonce states.
	for _, transactor := range transactors {
		// Obtain the L1 account nonce
		ctx, cancel := context.WithTimeout(context.Background(), txTimeoutDuration)
		endL1Nonce, err := l1Client.NonceAt(ctx, transactor.Account.L1Opts.From, nil)
		cancel()
		require.NoError(t, err)

		// Obtain the L2 sequencer account balance
		ctx, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
		endL2SeqBalance, err := l2Seq.BalanceAt(ctx, transactor.Account.L2Opts.From, nil)
		cancel()
		require.NoError(t, err)

		// Obtain the L2 sequencer account nonce
		ctx, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
		endL2SeqNonce, err := l2Seq.NonceAt(ctx, transactor.Account.L2Opts.From, nil)
		cancel()
		require.NoError(t, err)

		// Obtain the L2 verifier account balance
		ctx, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
		endL2VerifBalance, err := l2Verif.BalanceAt(ctx, transactor.Account.L2Opts.From, nil)
		cancel()
		require.NoError(t, err)

		// Obtain the L2 verifier account nonce
		ctx, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
		endL2VerifNonce, err := l2Verif.NonceAt(ctx, transactor.Account.L2Opts.From, nil)
		cancel()
		require.NoError(t, err)

		require.Equal(t, transactor.ExpectedL1Nonce, endL1Nonce, "Unexpected L1 nonce for transactor")
		require.Equal(t, transactor.ExpectedL2Nonce, endL2SeqNonce, "Unexpected L2 sequencer nonce for transactor")
		require.Equal(t, transactor.ExpectedL2Balance, endL2SeqBalance, "Unexpected L2 sequencer balance for transactor")
		require.Equal(t, transactor.ExpectedL2Nonce, endL2VerifNonce, "Unexpected L2 verifier nonce for transactor")
		require.Equal(t, transactor.ExpectedL2Balance, endL2VerifBalance, "Unexpected L2 verifier balance for transactor")
	}
}

269 270 271 272 273 274 275 276
func TestMixedWithdrawalValidity_L2OO(t *testing.T) {
	testMixedWithdrawalValidity(t, config.AllocTypeL2OO)
}

func TestMixedWithdrawalValidity_Standard(t *testing.T) {
	testMixedWithdrawalValidity(t, config.AllocTypeStandard)
}

277 278
// TestMixedWithdrawalValidity makes a number of withdrawal transactions and ensures ones with modified parameters are
// rejected while unmodified ones are accepted. This runs test cases in different systems.
279
func testMixedWithdrawalValidity(t *testing.T, allocType config.AllocType) {
280
	op_e2e.InitParallel(t)
281 282 283

	// There are 7 different fields we try modifying to cause a failure, plus one "good" test result we test.
	for i := 0; i <= 8; i++ {
284
		i := i // avoid loop var capture
285
		t.Run(fmt.Sprintf("withdrawal test#%d", i+1), func(t *testing.T) {
286 287 288
			ctx, bgCancel := context.WithCancel(context.Background())
			defer bgCancel()

289
			op_e2e.InitParallel(t)
Joshua Gutow's avatar
Joshua Gutow committed
290

291
			// Create our system configuration, funding all accounts we created for L1/L2, and start it
292
			cfg := e2esys.DefaultSystemConfig(t, e2esys.WithAllocType(allocType))
293
			cfg.Nodes["sequencer"].SafeDBPath = t.TempDir()
Mark Tyneway's avatar
Mark Tyneway committed
294 295 296
			cfg.DeployConfig.L2BlockTime = 2
			require.LessOrEqual(t, cfg.DeployConfig.FinalizationPeriodSeconds, uint64(6))
			require.Equal(t, cfg.DeployConfig.FundDevAccounts, true)
297
			sys, err := cfg.Start(t)
298 299 300
			require.NoError(t, err, "error starting up system")

			// Obtain our sequencer, verifier, and transactor keypair.
301 302 303
			l1Client := sys.NodeClient("l1")
			l2Seq := sys.NodeClient("sequencer")
			l2Verif := sys.NodeClient("verifier")
304 305
			require.NoError(t, err)

306
			systemConfig, err := legacybindings.NewSystemConfigCaller(cfg.L1Deployments.SystemConfigProxy, l1Client)
Mark Tyneway's avatar
Mark Tyneway committed
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
			require.NoError(t, err)
			unsafeBlockSigner, err := systemConfig.UnsafeBlockSigner(nil)
			require.NoError(t, err)
			require.Equal(t, cfg.DeployConfig.P2PSequencerAddress, unsafeBlockSigner)

			// The batcher has balance on L1
			batcherBalance, err := l1Client.BalanceAt(context.Background(), cfg.DeployConfig.BatchSenderAddress, nil)
			require.NoError(t, err)
			require.NotEqual(t, batcherBalance, big.NewInt(0))

			// The proposer has balance on L1
			proposerBalance, err := l1Client.BalanceAt(context.Background(), cfg.DeployConfig.L2OutputOracleProposer, nil)
			require.NoError(t, err)
			require.NotEqual(t, proposerBalance, big.NewInt(0))

322 323 324 325
			// Define our L1 transaction timeout duration.
			txTimeoutDuration := 10 * time.Duration(cfg.DeployConfig.L1BlockTime) * time.Second

			// Bind to the deposit contract
Mark Tyneway's avatar
Mark Tyneway committed
326
			depositContract, err := bindings.NewOptimismPortal(cfg.L1Deployments.OptimismPortalProxy, l1Client)
327 328 329
			_ = depositContract
			require.NoError(t, err)

330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
			var l2OutputOracle *bindings.L2OutputOracleCaller
			var disputeGameFactory *bindings.DisputeGameFactoryCaller
			var optimismPortal2 *bindingspreview.OptimismPortal2Caller
			if allocType.UsesProofs() {
				disputeGameFactory, err = bindings.NewDisputeGameFactoryCaller(cfg.L1Deployments.DisputeGameFactoryProxy, l1Client)
				require.NoError(t, err)
				optimismPortal2, err = bindingspreview.NewOptimismPortal2Caller(cfg.L1Deployments.OptimismPortalProxy, l1Client)
				require.NoError(t, err)
			} else {
				l2OutputOracle, err = bindings.NewL2OutputOracleCaller(cfg.L1Deployments.L2OutputOracleProxy, l1Client)
				require.NoError(t, err)
				finalizationPeriod, err := l2OutputOracle.FINALIZATIONPERIODSECONDS(nil)
				require.NoError(t, err)
				require.Equal(t, cfg.DeployConfig.FinalizationPeriodSeconds, finalizationPeriod.Uint64())
			}
345

346 347 348 349 350 351 352 353 354 355 356 357 358
			// Create a struct used to track our transactors and their transactions sent.
			type TestAccountState struct {
				Account           *TestAccount
				ExpectedL1Balance *big.Int
				ExpectedL2Balance *big.Int
				ExpectedL1Nonce   uint64
				ExpectedL2Nonce   uint64
			}

			// Create a test account state for our transactor.
			transactor := &TestAccountState{
				Account: &TestAccount{
					HDPath: e2eutils.DefaultMnemonicConfig.Alice,
Mark Tyneway's avatar
Mark Tyneway committed
359
					Key:    cfg.Secrets.Alice,
360 361 362 363 364 365 366 367 368 369 370 371 372 373
					L1Opts: nil,
					L2Opts: nil,
				},
				ExpectedL1Balance: nil,
				ExpectedL2Balance: nil,
				ExpectedL1Nonce:   0,
				ExpectedL2Nonce:   0,
			}
			transactor.Account.L1Opts, err = bind.NewKeyedTransactorWithChainID(transactor.Account.Key, cfg.L1ChainIDBig())
			require.NoError(t, err)
			transactor.Account.L2Opts, err = bind.NewKeyedTransactorWithChainID(transactor.Account.Key, cfg.L2ChainIDBig())
			require.NoError(t, err)

			// Obtain the transactor's starting balance on L1.
374 375 376
			txCtx, txCancel := context.WithTimeout(context.Background(), txTimeoutDuration)
			transactor.ExpectedL1Balance, err = l1Client.BalanceAt(txCtx, transactor.Account.L1Opts.From, nil)
			txCancel()
377 378 379
			require.NoError(t, err)

			// Obtain the transactor's starting balance on L2.
380 381 382
			txCtx, txCancel = context.WithTimeout(context.Background(), txTimeoutDuration)
			transactor.ExpectedL2Balance, err = l2Verif.BalanceAt(txCtx, transactor.Account.L2Opts.From, nil)
			txCancel()
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
			require.NoError(t, err)

			// Bind to the L2-L1 message passer
			l2l1MessagePasser, err := bindings.NewL2ToL1MessagePasser(predeploys.L2ToL1MessagePasserAddr, l2Seq)
			require.NoError(t, err, "error binding to message passer on L2")

			// Create our fuzzer wrapper to generate complex values (despite this not being a fuzz test, this is still a useful
			// provider to fill complex data structures).
			typeProvider := fuzz.NewWithSeed(time.Now().Unix()).NilChance(0).MaxDepth(10000).NumElements(0, 0x100)
			fuzzerutils.AddFuzzerFunctions(typeProvider)

			// Now we create a number of withdrawals from each transactor

			// Determine the address our request will come from
			fromAddr := crypto.PubkeyToAddress(transactor.Account.Key.PublicKey)
Mark Tyneway's avatar
Mark Tyneway committed
398 399 400
			fromBalance, err := l2Verif.BalanceAt(context.Background(), fromAddr, nil)
			require.NoError(t, err)
			require.Greaterf(t, fromBalance.Uint64(), uint64(700_000_000_000), "insufficient balance for %s", fromAddr)
401 402 403 404 405 406 407

			// Initiate Withdrawal
			withdrawAmount := big.NewInt(500_000_000_000)
			transactor.Account.L2Opts.Value = withdrawAmount
			tx, err := l2l1MessagePasser.InitiateWithdrawal(transactor.Account.L2Opts, fromAddr, big.NewInt(21000), nil)
			require.Nil(t, err, "sending initiate withdraw tx")

Mark Tyneway's avatar
Mark Tyneway committed
408
			t.Logf("Waiting for tx %s to be in sequencer", tx.Hash().Hex())
409
			receiptSeq, err := wait.ForReceiptOK(ctx, l2Seq, tx.Hash())
Mark Tyneway's avatar
Mark Tyneway committed
410 411 412 413 414 415
			require.Nil(t, err, "withdrawal initiated on L2 sequencer")

			verifierTip, err := l2Verif.BlockByNumber(context.Background(), nil)
			require.Nil(t, err)

			t.Logf("Waiting for tx %s to be in verifier. Verifier tip is %s:%d. Included in sequencer in block %s:%d", tx.Hash().Hex(), verifierTip.Hash().Hex(), verifierTip.NumberU64(), receiptSeq.BlockHash.Hex(), receiptSeq.BlockNumber)
416
			receipt, err := wait.ForReceiptOK(ctx, l2Verif, tx.Hash())
Mark Tyneway's avatar
Mark Tyneway committed
417
			require.Nilf(t, err, "withdrawal tx %s not found in verifier. included in block %s:%d", tx.Hash().Hex(), receiptSeq.BlockHash.Hex(), receiptSeq.BlockNumber)
418 419

			// Obtain the header for the block containing the transaction (used to calculate gas fees)
420 421 422
			txCtx, txCancel = context.WithTimeout(context.Background(), txTimeoutDuration)
			header, err := l2Verif.HeaderByNumber(txCtx, receipt.BlockNumber)
			txCancel()
423 424 425
			require.Nil(t, err)

			// Calculate gas fees for the withdrawal in L2 to later adjust our balance.
426
			withdrawalL2GasFee := helpers.CalcGasFees(receipt.GasUsed, tx.GasTipCap(), tx.GasFeeCap(), header.BaseFee)
427 428 429 430 431 432 433 434

			// Adjust our expected L2 balance (should've decreased by withdraw amount + fees)
			transactor.ExpectedL2Balance = new(big.Int).Sub(transactor.ExpectedL2Balance, withdrawAmount)
			transactor.ExpectedL2Balance = new(big.Int).Sub(transactor.ExpectedL2Balance, withdrawalL2GasFee)
			transactor.ExpectedL2Balance = new(big.Int).Sub(transactor.ExpectedL2Balance, receipt.L1Fee)
			transactor.ExpectedL2Nonce = transactor.ExpectedL2Nonce + 1

			// Wait for the finalization period, then we can finalize this withdrawal.
435
			var blockNumber uint64
436
			if allocType.UsesProofs() {
437 438 439 440
				blockNumber, err = wait.ForGamePublished(ctx, l1Client, cfg.L1Deployments.OptimismPortalProxy, cfg.L1Deployments.DisputeGameFactoryProxy, receipt.BlockNumber)
			} else {
				blockNumber, err = wait.ForOutputRootPublished(ctx, l1Client, cfg.L1Deployments.L2OutputOracleProxy, receipt.BlockNumber)
			}
441 442 443 444 445
			require.Nil(t, err)

			header, err = l2Verif.HeaderByNumber(ctx, new(big.Int).SetUint64(blockNumber))
			require.Nil(t, err)

446
			rpcClient, err := rpc.Dial(sys.EthInstances["verifier"].UserRPC().RPC())
447 448 449
			require.Nil(t, err)
			proofCl := gethclient.New(rpcClient)
			receiptCl := ethclient.NewClient(rpcClient)
450
			blockCl := ethclient.NewClient(rpcClient)
451 452

			// Now create the withdrawal
453
			params, err := helpers.ProveWithdrawalParameters(context.Background(), proofCl, receiptCl, blockCl, tx.Hash(), header, l2OutputOracle, disputeGameFactory, optimismPortal2, cfg.AllocType)
454 455 456
			require.Nil(t, err)

			// Obtain our withdrawal parameters
457
			withdrawal := crossdomain.Withdrawal{
458
				Nonce:    params.Nonce,
459 460
				Sender:   &params.Sender,
				Target:   &params.Target,
461 462 463 464
				Value:    params.Value,
				GasLimit: params.GasLimit,
				Data:     params.Data,
			}
465
			withdrawalTransaction := withdrawal.WithdrawalTransaction()
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527
			l2OutputIndexParam := params.L2OutputIndex
			outputRootProofParam := params.OutputRootProof
			withdrawalProofParam := params.WithdrawalProof

			// Determine if this will be a bad withdrawal.
			badWithdrawal := i < 8
			if badWithdrawal {
				// Select a field to overwrite depending on which test case this is.
				fieldIndex := i

				// We ensure that each field changes to something different.
				if fieldIndex == 0 {
					originalValue := new(big.Int).Set(withdrawalTransaction.Nonce)
					for originalValue.Cmp(withdrawalTransaction.Nonce) == 0 {
						typeProvider.Fuzz(&withdrawalTransaction.Nonce)
					}
				} else if fieldIndex == 1 {
					originalValue := withdrawalTransaction.Sender
					for originalValue == withdrawalTransaction.Sender {
						typeProvider.Fuzz(&withdrawalTransaction.Sender)
					}
				} else if fieldIndex == 2 {
					originalValue := withdrawalTransaction.Target
					for originalValue == withdrawalTransaction.Target {
						typeProvider.Fuzz(&withdrawalTransaction.Target)
					}
				} else if fieldIndex == 3 {
					originalValue := new(big.Int).Set(withdrawalTransaction.Value)
					for originalValue.Cmp(withdrawalTransaction.Value) == 0 {
						typeProvider.Fuzz(&withdrawalTransaction.Value)
					}
				} else if fieldIndex == 4 {
					originalValue := new(big.Int).Set(withdrawalTransaction.GasLimit)
					for originalValue.Cmp(withdrawalTransaction.GasLimit) == 0 {
						typeProvider.Fuzz(&withdrawalTransaction.GasLimit)
					}
				} else if fieldIndex == 5 {
					originalValue := new(big.Int).Set(l2OutputIndexParam)
					for originalValue.Cmp(l2OutputIndexParam) == 0 {
						typeProvider.Fuzz(&l2OutputIndexParam)
					}
				} else if fieldIndex == 6 {
					// TODO: this is a large structure that is unlikely to ever produce the same value, however we should
					//  verify that we actually generated different values.
					typeProvider.Fuzz(&outputRootProofParam)
				} else if fieldIndex == 7 {
					typeProvider.Fuzz(&withdrawalProofParam)
					originalValue := make([][]byte, len(withdrawalProofParam))
					for i := 0; i < len(withdrawalProofParam); i++ {
						originalValue[i] = make([]byte, len(withdrawalProofParam[i]))
						copy(originalValue[i], withdrawalProofParam[i])
						for bytes.Equal(originalValue[i], withdrawalProofParam[i]) {
							typeProvider.Fuzz(&withdrawalProofParam[i])
						}
					}

				}
			}

			// Prove withdrawal. This checks the proof so we only finalize if this succeeds
			tx, err = depositContract.ProveWithdrawalTransaction(
				transactor.Account.L1Opts,
528
				withdrawalTransaction,
529 530 531 532 533 534 535 536 537 538 539 540 541
				l2OutputIndexParam,
				outputRootProofParam,
				withdrawalProofParam,
			)

			// If we had a bad withdrawal, we don't update some expected value and skip to processing the next
			// withdrawal. Otherwise, if it was valid, this should've succeeded so we proceed with updating our expected
			// values and asserting no errors occurred.
			if badWithdrawal {
				require.Error(t, err)
			} else {
				require.NoError(t, err)

542
				if allocType.UsesProofs() {
543 544 545
					// Start a challenger to resolve claims and games once the clock expires
					factoryHelper := disputegame.NewFactoryHelper(t, ctx, sys)
					factoryHelper.StartChallenger(ctx, "Challenger",
546
						challenger.WithFastGames(),
547 548
						challenger.WithPrivKey(sys.Cfg.Secrets.Mallory))
				}
549
				receipt, err = wait.ForReceiptOK(ctx, l1Client, tx.Hash())
550
				require.NoError(t, err, "finalize withdrawal")
551 552 553

				// Verify balance after withdrawal
				header, err = l1Client.HeaderByNumber(ctx, receipt.BlockNumber)
554
				require.NoError(t, err)
555 556 557

				// Ensure that withdrawal - gas fees are added to the L1 balance
				// Fun fact, the fee is greater than the withdrawal amount
558
				withdrawalL1GasFee := helpers.CalcGasFees(receipt.GasUsed, tx.GasTipCap(), tx.GasFeeCap(), header.BaseFee)
559 560 561 562 563
				transactor.ExpectedL1Balance = new(big.Int).Add(transactor.ExpectedL2Balance, withdrawAmount)
				transactor.ExpectedL1Balance = new(big.Int).Sub(transactor.ExpectedL2Balance, withdrawalL1GasFee)
				transactor.ExpectedL1Nonce++

				// Ensure that our withdrawal was proved successfully
564
				_, err := wait.ForReceiptOK(ctx, l1Client, tx.Hash())
565
				require.NoError(t, err, "prove withdrawal")
566 567

				// Wait for finalization and then create the Finalized Withdrawal Transaction
568
				ctx, withdrawalCancel := context.WithTimeout(context.Background(), 60*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second)
569
				defer withdrawalCancel()
570
				if allocType.UsesProofs() {
571
					err = wait.ForWithdrawalCheck(ctx, l1Client, withdrawal, cfg.L1Deployments.OptimismPortalProxy, transactor.Account.L1Opts.From)
572
					require.NoError(t, err)
573 574
				} else {
					err = wait.ForFinalizationPeriod(ctx, l1Client, header.Number, cfg.L1Deployments.L2OutputOracleProxy)
575
					require.NoError(t, err)
576
				}
577 578 579 580

				// Finalize withdrawal
				_, err = depositContract.FinalizeWithdrawalTransaction(
					transactor.Account.L1Opts,
581
					withdrawalTransaction,
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
				)
				require.NoError(t, err)
			}

			// At the end, assert our account balance/nonce states.

			// Obtain the L2 sequencer account balance
			endL1Balance, err := l1Client.BalanceAt(ctx, transactor.Account.L1Opts.From, nil)
			require.NoError(t, err)

			// Obtain the L1 account nonce
			endL1Nonce, err := l1Client.NonceAt(ctx, transactor.Account.L1Opts.From, nil)
			require.NoError(t, err)

			// Obtain the L2 sequencer account balance
			endL2SeqBalance, err := l2Seq.BalanceAt(ctx, transactor.Account.L1Opts.From, nil)
			require.NoError(t, err)

			// Obtain the L2 sequencer account nonce
			endL2SeqNonce, err := l2Seq.NonceAt(ctx, transactor.Account.L1Opts.From, nil)
			require.NoError(t, err)

			// Obtain the L2 verifier account balance
			endL2VerifBalance, err := l2Verif.BalanceAt(ctx, transactor.Account.L1Opts.From, nil)
			require.NoError(t, err)

			// Obtain the L2 verifier account nonce
			endL2VerifNonce, err := l2Verif.NonceAt(ctx, transactor.Account.L1Opts.From, nil)
			require.NoError(t, err)

			// TODO: Check L1 balance as well here. We avoided this due to time constraints as it seems L1 fees
			//  were off slightly.
			_ = endL1Balance
615
			// require.Equal(t, transactor.ExpectedL1Balance, endL1Balance, "Unexpected L1 balance for transactor")
616 617 618 619 620 621 622 623
			require.Equal(t, transactor.ExpectedL1Nonce, endL1Nonce, "Unexpected L1 nonce for transactor")
			require.Equal(t, transactor.ExpectedL2Nonce, endL2SeqNonce, "Unexpected L2 sequencer nonce for transactor")
			require.Equal(t, transactor.ExpectedL2Balance, endL2SeqBalance, "Unexpected L2 sequencer balance for transactor")
			require.Equal(t, transactor.ExpectedL2Nonce, endL2VerifNonce, "Unexpected L2 verifier nonce for transactor")
			require.Equal(t, transactor.ExpectedL2Balance, endL2VerifBalance, "Unexpected L2 verifier balance for transactor")
		})
	}
}