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