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 }