github.com/cryptohub-digital/blockbook@v0.3.5-0.20240403155730-99ab40b9104c/tests/sync/sync.go (about)

     1  //go:build integration
     2  
     3  package sync
     4  
     5  import (
     6  	"encoding/json"
     7  	"errors"
     8  	"io/ioutil"
     9  	"os"
    10  	"path/filepath"
    11  	"testing"
    12  
    13  	"github.com/cryptohub-digital/blockbook/bchain"
    14  	"github.com/cryptohub-digital/blockbook/common"
    15  	"github.com/cryptohub-digital/blockbook/db"
    16  )
    17  
    18  var testMap = map[string]func(t *testing.T, th *TestHandler){
    19  	"ConnectBlocks":         testConnectBlocks,
    20  	"ConnectBlocksParallel": testConnectBlocksParallel,
    21  	"HandleFork":            testHandleFork,
    22  }
    23  
    24  type TestHandler struct {
    25  	Coin     string
    26  	Chain    bchain.BlockChain
    27  	TestData *TestData
    28  }
    29  
    30  type Range struct {
    31  	Lower uint32 `json:"lower"`
    32  	Upper uint32 `json:"upper"`
    33  }
    34  
    35  type TestData struct {
    36  	ConnectBlocks struct {
    37  		SyncRanges []Range              `json:"syncRanges"`
    38  		Blocks     map[uint32]BlockInfo `json:"blocks"`
    39  	} `json:"connectBlocks"`
    40  	HandleFork struct {
    41  		SyncRanges []Range            `json:"syncRanges"`
    42  		FakeBlocks map[uint32]BlockID `json:"fakeBlocks"`
    43  		RealBlocks map[uint32]BlockID `json:"realBlocks"`
    44  	} `json:"handleFork"`
    45  }
    46  
    47  type BlockID struct {
    48  	Height uint32 `json:"height"`
    49  	Hash   string `json:"hash"`
    50  }
    51  
    52  type BlockInfo struct {
    53  	BlockID
    54  	NoTxs     uint32       `json:"noTxs"`
    55  	TxDetails []*bchain.Tx `json:"txDetails"`
    56  }
    57  
    58  func IntegrationTest(t *testing.T, coin string, chain bchain.BlockChain, mempool bchain.Mempool, testConfig json.RawMessage) {
    59  	tests, err := getTests(testConfig)
    60  	if err != nil {
    61  		t.Fatalf("Failed loading of test list: %s", err)
    62  	}
    63  
    64  	parser := chain.GetChainParser()
    65  	td, err := loadTestData(coin, parser)
    66  	if err != nil {
    67  		t.Fatalf("Failed loading of test data: %s", err)
    68  	}
    69  
    70  	for _, test := range tests {
    71  		if f, found := testMap[test]; found {
    72  			h := TestHandler{Coin: coin, Chain: chain, TestData: td}
    73  			t.Run(test, func(t *testing.T) { f(t, &h) })
    74  		} else {
    75  			t.Errorf("%s: test not found", test)
    76  			continue
    77  		}
    78  	}
    79  }
    80  
    81  func getTests(cfg json.RawMessage) ([]string, error) {
    82  	var v []string
    83  	err := json.Unmarshal(cfg, &v)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	if len(v) == 0 {
    88  		return nil, errors.New("No tests declared")
    89  	}
    90  	return v, nil
    91  }
    92  
    93  func loadTestData(coin string, parser bchain.BlockChainParser) (*TestData, error) {
    94  	path := filepath.Join("sync/testdata", coin+".json")
    95  	b, err := ioutil.ReadFile(path)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  	var v TestData
   100  	err = json.Unmarshal(b, &v)
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  
   105  	for _, b := range v.ConnectBlocks.Blocks {
   106  		for _, tx := range b.TxDetails {
   107  			// convert amounts in test json to bit.Int and clear the temporary JsonValue
   108  			for i := range tx.Vout {
   109  				vout := &tx.Vout[i]
   110  				vout.ValueSat, err = parser.AmountToBigInt(vout.JsonValue)
   111  				if err != nil {
   112  					return nil, err
   113  				}
   114  				vout.JsonValue = ""
   115  			}
   116  
   117  			// get addresses parsed
   118  			err := setTxAddresses(parser, tx)
   119  			if err != nil {
   120  				return nil, err
   121  			}
   122  		}
   123  	}
   124  
   125  	return &v, nil
   126  }
   127  
   128  func setTxAddresses(parser bchain.BlockChainParser, tx *bchain.Tx) error {
   129  	for i := range tx.Vout {
   130  		ad, err := parser.GetAddrDescFromVout(&tx.Vout[i])
   131  		if err != nil {
   132  			return err
   133  		}
   134  		a, s, err := parser.GetAddressesFromAddrDesc(ad)
   135  		if err == nil && s {
   136  			tx.Vout[i].ScriptPubKey.Addresses = a
   137  		}
   138  	}
   139  	return nil
   140  }
   141  
   142  func makeRocksDB(parser bchain.BlockChainParser, m *common.Metrics, is *common.InternalState) (*db.RocksDB, func(), error) {
   143  	p, err := ioutil.TempDir("", "sync_test")
   144  	if err != nil {
   145  		return nil, nil, err
   146  	}
   147  
   148  	d, err := db.NewRocksDB(p, 1<<17, 1<<14, parser, m, false)
   149  	if err != nil {
   150  		return nil, nil, err
   151  	}
   152  
   153  	d.SetInternalState(is)
   154  
   155  	closer := func() {
   156  		d.Close()
   157  		os.RemoveAll(p)
   158  	}
   159  
   160  	return d, closer, nil
   161  }
   162  
   163  var metricsRegistry = map[string]*common.Metrics{}
   164  
   165  func getMetrics(name string) (*common.Metrics, error) {
   166  	if m, found := metricsRegistry[name]; found {
   167  		return m, nil
   168  	} else {
   169  		m, err := common.GetMetrics(name)
   170  		if err != nil {
   171  			return nil, err
   172  		}
   173  		metricsRegistry[name] = m
   174  		return m, nil
   175  	}
   176  }
   177  
   178  func withRocksDBAndSyncWorker(t *testing.T, h *TestHandler, startHeight uint32, fn func(*db.RocksDB, *db.SyncWorker, chan os.Signal)) {
   179  	m, err := getMetrics(h.Coin)
   180  	if err != nil {
   181  		t.Fatal(err)
   182  	}
   183  	is := &common.InternalState{}
   184  
   185  	d, closer, err := makeRocksDB(h.Chain.GetChainParser(), m, is)
   186  	if err != nil {
   187  		t.Fatal(err)
   188  	}
   189  	defer closer()
   190  
   191  	ch := make(chan os.Signal)
   192  
   193  	sw, err := db.NewSyncWorker(d, h.Chain, 8, 0, int(startHeight), false, ch, m, is)
   194  	if err != nil {
   195  		t.Fatal(err)
   196  	}
   197  
   198  	fn(d, sw, ch)
   199  }