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  }