github.com/lbryio/lbcd@v0.22.119/blockchain/common_test.go (about)

     1  // Copyright (c) 2013-2017 The btcsuite developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package blockchain
     6  
     7  import (
     8  	"bytes"
     9  	"compress/bzip2"
    10  	"encoding/binary"
    11  	"fmt"
    12  	"io"
    13  	"os"
    14  	"path/filepath"
    15  	"strings"
    16  	"time"
    17  
    18  	"github.com/lbryio/lbcd/chaincfg"
    19  	"github.com/lbryio/lbcd/chaincfg/chainhash"
    20  	"github.com/lbryio/lbcd/database"
    21  	_ "github.com/lbryio/lbcd/database/ffldb"
    22  	"github.com/lbryio/lbcd/txscript"
    23  	"github.com/lbryio/lbcd/wire"
    24  	btcutil "github.com/lbryio/lbcutil"
    25  )
    26  
    27  const (
    28  	// testDbType is the database backend type to use for the tests.
    29  	testDbType = "ffldb"
    30  
    31  	// testDbRoot is the root directory used to create all test databases.
    32  	testDbRoot = "testdbs"
    33  
    34  	// blockDataNet is the expected network in the test block data.
    35  	blockDataNet = wire.MainNet
    36  )
    37  
    38  // filesExists returns whether or not the named file or directory exists.
    39  func fileExists(name string) bool {
    40  	if _, err := os.Stat(name); err != nil {
    41  		if os.IsNotExist(err) {
    42  			return false
    43  		}
    44  	}
    45  	return true
    46  }
    47  
    48  // isSupportedDbType returns whether or not the passed database type is
    49  // currently supported.
    50  func isSupportedDbType(dbType string) bool {
    51  	supportedDrivers := database.SupportedDrivers()
    52  	for _, driver := range supportedDrivers {
    53  		if dbType == driver {
    54  			return true
    55  		}
    56  	}
    57  
    58  	return false
    59  }
    60  
    61  // loadBlocks reads files containing bitcoin block data (gzipped but otherwise
    62  // in the format bitcoind writes) from disk and returns them as an array of
    63  // btcutil.Block.  This is largely borrowed from the test code in btcdb.
    64  func loadBlocks(filename string) (blocks []*btcutil.Block, err error) {
    65  	filename = filepath.Join("testdata/", filename)
    66  
    67  	var network = 0xd9b4bef9 // bitcoin's network ID
    68  	var dr io.Reader
    69  	var fi io.ReadCloser
    70  
    71  	fi, err = os.Open(filename)
    72  	if err != nil {
    73  		return blocks, err
    74  	}
    75  
    76  	if strings.HasSuffix(filename, ".bz2") {
    77  		dr = bzip2.NewReader(fi)
    78  	} else {
    79  		dr = fi
    80  	}
    81  	defer fi.Close()
    82  
    83  	var block *btcutil.Block
    84  
    85  	err = nil
    86  	for height := int64(1); err == nil; height++ {
    87  		var rintbuf uint32
    88  		err = binary.Read(dr, binary.LittleEndian, &rintbuf)
    89  		if err == io.EOF {
    90  			// hit end of file at expected offset: no warning
    91  			height--
    92  			err = nil
    93  			break
    94  		}
    95  		if err != nil {
    96  			break
    97  		}
    98  		if rintbuf != uint32(network) {
    99  			continue
   100  		}
   101  		err = binary.Read(dr, binary.LittleEndian, &rintbuf)
   102  		blocklen := rintbuf
   103  
   104  		rbytes := make([]byte, blocklen)
   105  
   106  		// read block
   107  		dr.Read(rbytes)
   108  
   109  		// inject claimtrie:
   110  		tail := make([]byte, len(rbytes)-68)
   111  		copy(tail, rbytes[68:])
   112  		rbytes = append(rbytes[:68], bytes.Repeat([]byte{23}, chainhash.HashSize)...)
   113  		rbytes = append(rbytes, tail...)
   114  
   115  		block, err = btcutil.NewBlockFromBytes(rbytes)
   116  		if err != nil {
   117  			return blocks, err
   118  		}
   119  		blocks = append(blocks, block)
   120  	}
   121  
   122  	return blocks, err
   123  }
   124  
   125  // chainSetup is used to create a new db and chain instance with the genesis
   126  // block already inserted.  In addition to the new chain instance, it returns
   127  // a teardown function the caller should invoke when done testing to clean up.
   128  func chainSetup(dbName string, params *chaincfg.Params) (*BlockChain, func(), error) {
   129  	if !isSupportedDbType(testDbType) {
   130  		return nil, nil, fmt.Errorf("unsupported db type %v", testDbType)
   131  	}
   132  
   133  	// Handle memory database specially since it doesn't need the disk
   134  	// specific handling.
   135  	var db database.DB
   136  	var teardown func()
   137  	if testDbType == "memdb" {
   138  		ndb, err := database.Create(testDbType)
   139  		if err != nil {
   140  			return nil, nil, fmt.Errorf("error creating db: %v", err)
   141  		}
   142  		db = ndb
   143  
   144  		// Setup a teardown function for cleaning up.  This function is
   145  		// returned to the caller to be invoked when it is done testing.
   146  		teardown = func() {
   147  			db.Close()
   148  		}
   149  	} else {
   150  		// Create the root directory for test databases.
   151  		if !fileExists(testDbRoot) {
   152  			if err := os.MkdirAll(testDbRoot, 0700); err != nil {
   153  				err := fmt.Errorf("unable to create test db "+
   154  					"root: %v", err)
   155  				return nil, nil, err
   156  			}
   157  		}
   158  
   159  		// Create a new database to store the accepted blocks into.
   160  		dbPath := filepath.Join(testDbRoot, dbName)
   161  		_ = os.RemoveAll(dbPath)
   162  		ndb, err := database.Create(testDbType, dbPath, blockDataNet)
   163  		if err != nil {
   164  			return nil, nil, fmt.Errorf("error creating db: %v", err)
   165  		}
   166  		db = ndb
   167  
   168  		// Setup a teardown function for cleaning up.  This function is
   169  		// returned to the caller to be invoked when it is done testing.
   170  		teardown = func() {
   171  			db.Close()
   172  			os.RemoveAll(dbPath)
   173  			os.RemoveAll(testDbRoot)
   174  		}
   175  	}
   176  
   177  	// Copy the chain params to ensure any modifications the tests do to
   178  	// the chain parameters do not affect the global instance.
   179  	paramsCopy := *params
   180  
   181  	// Create the main chain instance.
   182  	chain, err := New(&Config{
   183  		DB:          db,
   184  		ChainParams: &paramsCopy,
   185  		Checkpoints: nil,
   186  		TimeSource:  NewMedianTime(),
   187  		SigCache:    txscript.NewSigCache(1000),
   188  	})
   189  	if err != nil {
   190  		teardown()
   191  		err := fmt.Errorf("failed to create chain instance: %v", err)
   192  		return nil, nil, err
   193  	}
   194  	return chain, teardown, nil
   195  }
   196  
   197  // loadUtxoView returns a utxo view loaded from a file.
   198  func loadUtxoView(filename string) (*UtxoViewpoint, error) {
   199  	// The utxostore file format is:
   200  	// <tx hash><output index><serialized utxo len><serialized utxo>
   201  	//
   202  	// The output index and serialized utxo len are little endian uint32s
   203  	// and the serialized utxo uses the format described in chainio.go.
   204  
   205  	filename = filepath.Join("testdata", filename)
   206  	fi, err := os.Open(filename)
   207  	if err != nil {
   208  		return nil, err
   209  	}
   210  
   211  	// Choose read based on whether the file is compressed or not.
   212  	var r io.Reader
   213  	if strings.HasSuffix(filename, ".bz2") {
   214  		r = bzip2.NewReader(fi)
   215  	} else {
   216  		r = fi
   217  	}
   218  	defer fi.Close()
   219  
   220  	view := NewUtxoViewpoint()
   221  	for {
   222  		// Hash of the utxo entry.
   223  		var hash chainhash.Hash
   224  		_, err := io.ReadAtLeast(r, hash[:], len(hash[:]))
   225  		if err != nil {
   226  			// Expected EOF at the right offset.
   227  			if err == io.EOF {
   228  				break
   229  			}
   230  			return nil, err
   231  		}
   232  
   233  		// Output index of the utxo entry.
   234  		var index uint32
   235  		err = binary.Read(r, binary.LittleEndian, &index)
   236  		if err != nil {
   237  			return nil, err
   238  		}
   239  
   240  		// Num of serialized utxo entry bytes.
   241  		var numBytes uint32
   242  		err = binary.Read(r, binary.LittleEndian, &numBytes)
   243  		if err != nil {
   244  			return nil, err
   245  		}
   246  
   247  		// Serialized utxo entry.
   248  		serialized := make([]byte, numBytes)
   249  		_, err = io.ReadAtLeast(r, serialized, int(numBytes))
   250  		if err != nil {
   251  			return nil, err
   252  		}
   253  
   254  		// Deserialize it and add it to the view.
   255  		entry, err := deserializeUtxoEntry(serialized)
   256  		if err != nil {
   257  			return nil, err
   258  		}
   259  		view.Entries()[wire.OutPoint{Hash: hash, Index: index}] = entry
   260  	}
   261  
   262  	return view, nil
   263  }
   264  
   265  // convertUtxoStore reads a utxostore from the legacy format and writes it back
   266  // out using the latest format.  It is only useful for converting utxostore data
   267  // used in the tests, which has already been done.  However, the code is left
   268  // available for future reference.
   269  func convertUtxoStore(r io.Reader, w io.Writer) error {
   270  	// The old utxostore file format was:
   271  	// <tx hash><serialized utxo len><serialized utxo>
   272  	//
   273  	// The serialized utxo len was a little endian uint32 and the serialized
   274  	// utxo uses the format described in upgrade.go.
   275  
   276  	littleEndian := binary.LittleEndian
   277  	for {
   278  		// Hash of the utxo entry.
   279  		var hash chainhash.Hash
   280  		_, err := io.ReadAtLeast(r, hash[:], len(hash[:]))
   281  		if err != nil {
   282  			// Expected EOF at the right offset.
   283  			if err == io.EOF {
   284  				break
   285  			}
   286  			return err
   287  		}
   288  
   289  		// Num of serialized utxo entry bytes.
   290  		var numBytes uint32
   291  		err = binary.Read(r, littleEndian, &numBytes)
   292  		if err != nil {
   293  			return err
   294  		}
   295  
   296  		// Serialized utxo entry.
   297  		serialized := make([]byte, numBytes)
   298  		_, err = io.ReadAtLeast(r, serialized, int(numBytes))
   299  		if err != nil {
   300  			return err
   301  		}
   302  
   303  		// Deserialize the entry.
   304  		entries, err := deserializeUtxoEntryV0(serialized)
   305  		if err != nil {
   306  			return err
   307  		}
   308  
   309  		// Loop through all of the utxos and write them out in the new
   310  		// format.
   311  		for outputIdx, entry := range entries {
   312  			// Reserialize the entries using the new format.
   313  			serialized, err := serializeUtxoEntry(entry)
   314  			if err != nil {
   315  				return err
   316  			}
   317  
   318  			// Write the hash of the utxo entry.
   319  			_, err = w.Write(hash[:])
   320  			if err != nil {
   321  				return err
   322  			}
   323  
   324  			// Write the output index of the utxo entry.
   325  			err = binary.Write(w, littleEndian, outputIdx)
   326  			if err != nil {
   327  				return err
   328  			}
   329  
   330  			// Write num of serialized utxo entry bytes.
   331  			err = binary.Write(w, littleEndian, uint32(len(serialized)))
   332  			if err != nil {
   333  				return err
   334  			}
   335  
   336  			// Write the serialized utxo.
   337  			_, err = w.Write(serialized)
   338  			if err != nil {
   339  				return err
   340  			}
   341  		}
   342  	}
   343  
   344  	return nil
   345  }
   346  
   347  // TstSetCoinbaseMaturity makes the ability to set the coinbase maturity
   348  // available when running tests.
   349  func (b *BlockChain) TstSetCoinbaseMaturity(maturity uint16) {
   350  	b.chainParams.CoinbaseMaturity = maturity
   351  }
   352  
   353  // newFakeChain returns a chain that is usable for syntetic tests.  It is
   354  // important to note that this chain has no database associated with it, so
   355  // it is not usable with all functions and the tests must take care when making
   356  // use of it.
   357  func newFakeChain(params *chaincfg.Params) *BlockChain {
   358  	// Create a genesis block node and block index index populated with it
   359  	// for use when creating the fake chain below.
   360  	node := newBlockNode(&params.GenesisBlock.Header, nil)
   361  	index := newBlockIndex(nil, params)
   362  	index.AddNode(node)
   363  
   364  	targetTimespan := int64(params.TargetTimespan / time.Second)
   365  	targetTimePerBlock := int64(params.TargetTimePerBlock / time.Second)
   366  	adjustmentFactor := params.RetargetAdjustmentFactor
   367  	return &BlockChain{
   368  		chainParams:         params,
   369  		timeSource:          NewMedianTime(),
   370  		minRetargetTimespan: targetTimespan / adjustmentFactor,
   371  		maxRetargetTimespan: targetTimespan * adjustmentFactor,
   372  		blocksPerRetarget:   int32(targetTimespan / targetTimePerBlock),
   373  		index:               index,
   374  		bestChain:           newChainView(node),
   375  		warningCaches:       newThresholdCaches(vbNumBits),
   376  		deploymentCaches:    newThresholdCaches(chaincfg.DefinedDeployments),
   377  	}
   378  }
   379  
   380  // newFakeNode creates a block node connected to the passed parent with the
   381  // provided fields populated and fake values for the other fields.
   382  func newFakeNode(parent *blockNode, blockVersion int32, bits uint32, timestamp time.Time) *blockNode {
   383  	// Make up a header and create a block node from it.
   384  	header := &wire.BlockHeader{
   385  		Version:   blockVersion,
   386  		PrevBlock: parent.hash,
   387  		Bits:      bits,
   388  		Timestamp: timestamp,
   389  	}
   390  	return newBlockNode(header, parent)
   391  }