github.com/ethereum-optimism/optimism@v1.7.2/op-node/rollup/derive/deposit_log_tob_test.go (about)

     1  package derive
     2  
     3  import (
     4  	"math/big"
     5  	"testing"
     6  
     7  	"github.com/ethereum-optimism/optimism/op-service/testutils"
     8  	"github.com/ethereum-optimism/optimism/op-service/testutils/fuzzerutils"
     9  	"github.com/ethereum/go-ethereum/common"
    10  	"github.com/ethereum/go-ethereum/core/types"
    11  	fuzz "github.com/google/gofuzz"
    12  	"github.com/stretchr/testify/require"
    13  )
    14  
    15  // fuzzReceipts is similar to makeReceipts except it uses the fuzzer to populate DepositTx fields.
    16  func fuzzReceipts(typeProvider *fuzz.Fuzzer, blockHash common.Hash, depositContractAddr common.Address) (receipts []*types.Receipt, expectedDeposits []*types.DepositTx) {
    17  	// Determine how many receipts to generate (capped)
    18  	var receiptCount uint64
    19  	typeProvider.Fuzz(&receiptCount)
    20  
    21  	// Cap our receipt count otherwise we might generate for too long and our fuzzer will assume we hung
    22  	if receiptCount > 0x10 {
    23  		receiptCount = 0x10
    24  	}
    25  
    26  	// Create every receipt we intend to
    27  	logIndex := uint(0)
    28  	for i := uint64(0); i < receiptCount; i++ {
    29  		// Obtain our fuzz parameters for generating this receipt
    30  		var txReceiptValues struct {
    31  			GoodReceipt bool
    32  			DepositLogs []bool
    33  		}
    34  		typeProvider.Fuzz(&txReceiptValues)
    35  
    36  		// Generate a list of transaction receipts
    37  		var logs []*types.Log
    38  		status := types.ReceiptStatusSuccessful
    39  		if txReceiptValues.GoodReceipt {
    40  			status = types.ReceiptStatusFailed
    41  		}
    42  
    43  		// Determine if this log will be a deposit log or not and generate it accordingly
    44  		for _, isDeposit := range txReceiptValues.DepositLogs {
    45  			var ev *types.Log
    46  			var err error
    47  			if isDeposit {
    48  				// Generate a user deposit source
    49  				source := UserDepositSource{L1BlockHash: blockHash, LogIndex: uint64(logIndex)}
    50  
    51  				// Fuzz parameters to construct our deposit log
    52  				var fuzzedDepositInfo struct {
    53  					FromAddr *common.Address
    54  					ToAddr   *common.Address
    55  					Value    *big.Int
    56  					Gas      uint64
    57  					Data     []byte
    58  					Mint     *big.Int
    59  				}
    60  				typeProvider.Fuzz(&fuzzedDepositInfo)
    61  
    62  				// Create our deposit transaction
    63  				dep := &types.DepositTx{
    64  					SourceHash:          source.SourceHash(),
    65  					From:                *fuzzedDepositInfo.FromAddr,
    66  					To:                  fuzzedDepositInfo.ToAddr,
    67  					Value:               fuzzedDepositInfo.Value,
    68  					Gas:                 fuzzedDepositInfo.Gas,
    69  					Data:                fuzzedDepositInfo.Data,
    70  					Mint:                fuzzedDepositInfo.Mint,
    71  					IsSystemTransaction: false,
    72  				}
    73  
    74  				// Marshal our actual log event
    75  				ev, err = MarshalDepositLogEvent(depositContractAddr, dep)
    76  				if err != nil {
    77  					panic(err)
    78  				}
    79  
    80  				// If we have a good version and our tx succeeded, we add this to our list of expected deposits to
    81  				// return.
    82  				if status == types.ReceiptStatusSuccessful {
    83  					expectedDeposits = append(expectedDeposits, dep)
    84  				}
    85  			} else {
    86  				// If we're generated an unrelated log event (not deposit), fuzz some random parameters to use.
    87  				var randomUnrelatedLogInfo struct {
    88  					Addr   *common.Address
    89  					Topics []common.Hash
    90  					Data   []byte
    91  				}
    92  				typeProvider.Fuzz(&randomUnrelatedLogInfo)
    93  
    94  				// Generate the random log
    95  				ev = testutils.GenerateLog(*randomUnrelatedLogInfo.Addr, randomUnrelatedLogInfo.Topics, randomUnrelatedLogInfo.Data)
    96  			}
    97  			ev.TxIndex = uint(i)
    98  			ev.Index = logIndex
    99  			ev.BlockHash = blockHash
   100  			logs = append(logs, ev)
   101  			logIndex++
   102  		}
   103  
   104  		// Add our receipt to our list
   105  		receipts = append(receipts, &types.Receipt{
   106  			Type:             types.DynamicFeeTxType,
   107  			Status:           status,
   108  			Logs:             logs,
   109  			BlockHash:        blockHash,
   110  			TransactionIndex: uint(i),
   111  		})
   112  	}
   113  	return
   114  }
   115  
   116  // FuzzDeriveDepositsRoundTrip tests the derivation of deposits from transaction receipt event logs. It mixes
   117  // valid and invalid deposit transactions and ensures all valid deposits are derived as expected.
   118  // This is a fuzz test corresponding to TestDeriveUserDeposits.
   119  func FuzzDeriveDepositsRoundTrip(f *testing.F) {
   120  	f.Fuzz(func(t *testing.T, fuzzedData []byte) {
   121  		// Create our fuzzer wrapper to generate complex values
   122  		typeProvider := fuzz.NewFromGoFuzz(fuzzedData).NilChance(0).MaxDepth(10000).NumElements(0, 0x100).Funcs(
   123  			func(e *big.Int, c fuzz.Continue) {
   124  				var temp [32]byte
   125  				c.Fuzz(&temp)
   126  				e.SetBytes(temp[:])
   127  			},
   128  			func(e *common.Hash, c fuzz.Continue) {
   129  				var temp [32]byte
   130  				c.Fuzz(&temp)
   131  				e.SetBytes(temp[:])
   132  			},
   133  			func(e *common.Address, c fuzz.Continue) {
   134  				var temp [20]byte
   135  				c.Fuzz(&temp)
   136  				e.SetBytes(temp[:])
   137  			})
   138  
   139  		// Create a dummy block hash for this block
   140  		var blockHash common.Hash
   141  		typeProvider.Fuzz(&blockHash)
   142  
   143  		// Fuzz to generate some random deposit events
   144  		receipts, expectedDeposits := fuzzReceipts(typeProvider, blockHash, MockDepositContractAddr)
   145  
   146  		// Derive our user deposits from the transaction receipts
   147  		derivedDeposits, err := UserDeposits(receipts, MockDepositContractAddr)
   148  		require.NoError(t, err)
   149  
   150  		// Ensure all deposits we derived matched what we expected to receive.
   151  		require.Equal(t, len(derivedDeposits), len(expectedDeposits))
   152  		for i, derivedDeposit := range derivedDeposits {
   153  			expectedDeposit := expectedDeposits[i]
   154  			require.Equal(t, expectedDeposit, derivedDeposit)
   155  		}
   156  	})
   157  }
   158  
   159  // FuzzDeriveDepositsBadVersion ensures that if a deposit transaction receipt event log specifies an invalid deposit
   160  // version, no deposits should be derived.
   161  func FuzzDeriveDepositsBadVersion(f *testing.F) {
   162  	f.Fuzz(func(t *testing.T, fuzzedData []byte) {
   163  		// Create our fuzzer wrapper to generate complex values
   164  		typeProvider := fuzz.NewFromGoFuzz(fuzzedData).NilChance(0).MaxDepth(10000).NumElements(0, 0x100)
   165  		fuzzerutils.AddFuzzerFunctions(typeProvider)
   166  
   167  		// Create a dummy block hash for this block
   168  		var blockHash common.Hash
   169  		typeProvider.Fuzz(&blockHash)
   170  
   171  		// Fuzz to generate some random deposit events
   172  		receipts, _ := fuzzReceipts(typeProvider, blockHash, MockDepositContractAddr)
   173  
   174  		// Loop through all receipt logs and let the fuzzer determine which (if any) to patch.
   175  		hasBadDepositVersion := false
   176  		for _, receipt := range receipts {
   177  
   178  			// TODO: Using a hardcoded index (Topics[3]) here is not ideal. The MarshalDepositLogEvent method should
   179  			//  be spliced apart to be more configurable for these tests.
   180  
   181  			// Loop for each log in this receipt and check if it has a deposit event from our contract
   182  			for _, log := range receipt.Logs {
   183  				if log.Address == MockDepositContractAddr && len(log.Topics) >= 4 && log.Topics[0] == DepositEventABIHash {
   184  					// Determine if we should set a bad deposit version for this log
   185  					var patchBadDeposit bool
   186  					typeProvider.Fuzz(&patchBadDeposit)
   187  					if patchBadDeposit {
   188  						// Generate any topic but the deposit event versions we support.
   189  						// TODO: As opposed to keeping this hardcoded, a method such as IsValidVersion(v) should be
   190  						//  used here.
   191  						badTopic := DepositEventVersion0
   192  						for badTopic == DepositEventVersion0 {
   193  							typeProvider.Fuzz(&badTopic)
   194  						}
   195  
   196  						// Set our bad topic and update our state
   197  						log.Topics[3] = badTopic
   198  						hasBadDepositVersion = true
   199  					}
   200  				}
   201  			}
   202  		}
   203  
   204  		// Derive our user deposits from the transaction receipts
   205  		_, err := UserDeposits(receipts, MockDepositContractAddr)
   206  
   207  		// If we patched a bad deposit version this iteration, we should expect an error and not be able to proceed
   208  		// further
   209  		if hasBadDepositVersion {
   210  			require.Errorf(t, err, "")
   211  			return
   212  		}
   213  		require.NoError(t, err, "")
   214  	})
   215  }