common_test.go raw

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