github.com/MetalBlockchain/subnet-evm@v0.4.9/sync/statesync/code_syncer_test.go (about) 1 // (c) 2021-2022, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package statesync 5 6 import ( 7 "context" 8 "errors" 9 "testing" 10 11 "github.com/MetalBlockchain/metalgo/utils" 12 "github.com/MetalBlockchain/subnet-evm/core/rawdb" 13 "github.com/MetalBlockchain/subnet-evm/ethdb/memorydb" 14 "github.com/MetalBlockchain/subnet-evm/plugin/evm/message" 15 statesyncclient "github.com/MetalBlockchain/subnet-evm/sync/client" 16 "github.com/MetalBlockchain/subnet-evm/sync/handlers" 17 handlerstats "github.com/MetalBlockchain/subnet-evm/sync/handlers/stats" 18 "github.com/ethereum/go-ethereum/common" 19 "github.com/ethereum/go-ethereum/crypto" 20 "github.com/stretchr/testify/assert" 21 ) 22 23 type codeSyncerTest struct { 24 setupCodeSyncer func(*codeSyncer) 25 codeRequestHashes [][]common.Hash 26 codeByteSlices [][]byte 27 getCodeIntercept func(hashes []common.Hash, codeBytes [][]byte) ([][]byte, error) 28 err error 29 } 30 31 func testCodeSyncer(t *testing.T, test codeSyncerTest) { 32 // Set up serverDB 33 serverDB := memorydb.New() 34 35 codeHashes := make([]common.Hash, 0, len(test.codeByteSlices)) 36 for _, codeBytes := range test.codeByteSlices { 37 codeHash := crypto.Keccak256Hash(codeBytes) 38 rawdb.WriteCode(serverDB, codeHash, codeBytes) 39 codeHashes = append(codeHashes, codeHash) 40 } 41 42 // Set up mockClient 43 codeRequestHandler := handlers.NewCodeRequestHandler(serverDB, message.Codec, handlerstats.NewNoopHandlerStats()) 44 mockClient := statesyncclient.NewMockClient(message.Codec, nil, codeRequestHandler, nil) 45 mockClient.GetCodeIntercept = test.getCodeIntercept 46 47 clientDB := memorydb.New() 48 49 codeSyncer := newCodeSyncer(CodeSyncerConfig{ 50 MaxOutstandingCodeHashes: DefaultMaxOutstandingCodeHashes, 51 NumCodeFetchingWorkers: DefaultNumCodeFetchingWorkers, 52 Client: mockClient, 53 DB: clientDB, 54 }) 55 if test.setupCodeSyncer != nil { 56 test.setupCodeSyncer(codeSyncer) 57 } 58 codeSyncer.start(context.Background()) 59 60 for _, codeHashes := range test.codeRequestHashes { 61 if err := codeSyncer.addCode(codeHashes); err != nil { 62 if test.err == nil { 63 t.Fatal(err) 64 } else { 65 assert.ErrorIs(t, err, test.err) 66 } 67 } 68 } 69 codeSyncer.notifyAccountTrieCompleted() 70 71 err := <-codeSyncer.Done() 72 if test.err != nil { 73 if err == nil { 74 t.Fatal(t, "expected non-nil error: %s", test.err) 75 } 76 assert.ErrorIs(t, err, test.err) 77 return 78 } else if err != nil { 79 t.Fatal(err) 80 } 81 82 // Assert that the client synced the code correctly. 83 for i, codeHash := range codeHashes { 84 codeBytes := rawdb.ReadCode(clientDB, codeHash) 85 assert.Equal(t, test.codeByteSlices[i], codeBytes) 86 } 87 } 88 89 func TestCodeSyncerSingleCodeHash(t *testing.T) { 90 codeBytes := utils.RandomBytes(100) 91 codeHash := crypto.Keccak256Hash(codeBytes) 92 testCodeSyncer(t, codeSyncerTest{ 93 codeRequestHashes: [][]common.Hash{{codeHash}}, 94 codeByteSlices: [][]byte{codeBytes}, 95 }) 96 } 97 98 func TestCodeSyncerManyCodeHashes(t *testing.T) { 99 numCodeSlices := 5000 100 codeHashes := make([]common.Hash, 0, numCodeSlices) 101 codeByteSlices := make([][]byte, 0, numCodeSlices) 102 for i := 0; i < numCodeSlices; i++ { 103 codeBytes := utils.RandomBytes(100) 104 codeHash := crypto.Keccak256Hash(codeBytes) 105 codeHashes = append(codeHashes, codeHash) 106 codeByteSlices = append(codeByteSlices, codeBytes) 107 } 108 109 testCodeSyncer(t, codeSyncerTest{ 110 setupCodeSyncer: func(c *codeSyncer) { 111 c.codeHashes = make(chan common.Hash, 10) 112 }, 113 codeRequestHashes: [][]common.Hash{codeHashes[0:100], codeHashes[100:2000], codeHashes[2000:2005], codeHashes[2005:]}, 114 codeByteSlices: codeByteSlices, 115 }) 116 } 117 118 func TestCodeSyncerRequestErrors(t *testing.T) { 119 codeBytes := utils.RandomBytes(100) 120 codeHash := crypto.Keccak256Hash(codeBytes) 121 err := errors.New("dummy error") 122 testCodeSyncer(t, codeSyncerTest{ 123 codeRequestHashes: [][]common.Hash{{codeHash}}, 124 codeByteSlices: [][]byte{codeBytes}, 125 getCodeIntercept: func(hashes []common.Hash, codeBytes [][]byte) ([][]byte, error) { 126 return nil, err 127 }, 128 err: err, 129 }) 130 } 131 132 func TestCodeSyncerAddsInProgressCodeHashes(t *testing.T) { 133 codeBytes := utils.RandomBytes(100) 134 codeHash := crypto.Keccak256Hash(codeBytes) 135 testCodeSyncer(t, codeSyncerTest{ 136 setupCodeSyncer: func(c *codeSyncer) { 137 rawdb.AddCodeToFetch(c.DB, codeHash) 138 }, 139 codeRequestHashes: nil, 140 codeByteSlices: [][]byte{codeBytes}, 141 }) 142 } 143 144 func TestCodeSyncerAddsMoreInProgressThanQueueSize(t *testing.T) { 145 numCodeSlices := 10 146 codeHashes := make([]common.Hash, 0, numCodeSlices) 147 codeByteSlices := make([][]byte, 0, numCodeSlices) 148 for i := 0; i < numCodeSlices; i++ { 149 codeBytes := utils.RandomBytes(100) 150 codeHash := crypto.Keccak256Hash(codeBytes) 151 codeHashes = append(codeHashes, codeHash) 152 codeByteSlices = append(codeByteSlices, codeBytes) 153 } 154 155 testCodeSyncer(t, codeSyncerTest{ 156 setupCodeSyncer: func(c *codeSyncer) { 157 for _, codeHash := range codeHashes { 158 rawdb.AddCodeToFetch(c.DB, codeHash) 159 } 160 c.codeHashes = make(chan common.Hash, numCodeSlices/2) 161 }, 162 codeRequestHashes: nil, 163 codeByteSlices: codeByteSlices, 164 }) 165 }