Esempio n. 1
0
// newBlockImporter returns a new importer for the provided file reader seeker
// and database.
func newBlockImporter(db database.DB, r io.ReadSeeker) (*blockImporter, error) {
	// Create the transaction and address indexes if needed.
	//
	// CAUTION: the txindex needs to be first in the indexes array because
	// the addrindex uses data from the txindex during catchup.  If the
	// addrindex is run first, it may not have the transactions from the
	// current block indexed.
	var indexes []indexers.Indexer
	if cfg.TxIndex || cfg.AddrIndex {
		// Enable transaction index if address index is enabled since it
		// requires it.
		if !cfg.TxIndex {
			log.Infof("Transaction index enabled because it is " +
				"required by the address index")
			cfg.TxIndex = true
		} else {
			log.Info("Transaction index is enabled")
		}
		indexes = append(indexes, indexers.NewTxIndex(db))
	}
	if cfg.AddrIndex {
		log.Info("Address index is enabled")
		indexes = append(indexes, indexers.NewAddrIndex(db, activeNetParams))
	}

	// Create an index manager if any of the optional indexes are enabled.
	var indexManager blockchain.IndexManager
	if len(indexes) > 0 {
		indexManager = indexers.NewManager(db, indexes)
	}

	chain, err := blockchain.New(&blockchain.Config{
		DB:           db,
		ChainParams:  activeNetParams,
		TimeSource:   blockchain.NewMedianTime(),
		IndexManager: indexManager,
	})
	if err != nil {
		return nil, err
	}

	return &blockImporter{
		db:           db,
		r:            r,
		processQueue: make(chan []byte, 2),
		doneChan:     make(chan bool),
		errChan:      make(chan error),
		quit:         make(chan struct{}),
		chain:        chain,
		lastLogTime:  time.Now(),
	}, nil
}
Esempio n. 2
0
// This example demonstrates how to create a new chain instance and use
// ProcessBlock to attempt to attempt add a block to the chain.  As the package
// overview documentation describes, this includes all of the Bitcoin consensus
// rules.  This example intentionally attempts to insert a duplicate genesis
// block to illustrate how an invalid block is handled.
func ExampleBlockChain_ProcessBlock() {
	// Create a new database to store the accepted blocks into.  Typically
	// this would be opening an existing database and would not be deleting
	// and creating a new database like this, but it is done here so this is
	// a complete working example and does not leave temporary files laying
	// around.
	dbPath := filepath.Join(os.TempDir(), "exampleprocessblock")
	_ = os.RemoveAll(dbPath)
	db, err := database.Create("ffldb", dbPath, chaincfg.MainNetParams.Net)
	if err != nil {
		fmt.Printf("Failed to create database: %v\n", err)
		return
	}
	defer os.RemoveAll(dbPath)
	defer db.Close()

	// Create a new BlockChain instance using the underlying database for
	// the main bitcoin network.  This example does not demonstrate some
	// of the other available configuration options such as specifying a
	// notification callback and signature cache.  Also, the caller would
	// ordinarily keep a reference to the median time source and add time
	// values obtained from other peers on the network so the local time is
	// adjusted to be in agreement with other peers.
	chain, err := blockchain.New(&blockchain.Config{
		DB:          db,
		ChainParams: &chaincfg.MainNetParams,
		TimeSource:  blockchain.NewMedianTime(),
	})
	if err != nil {
		fmt.Printf("Failed to create chain instance: %v\n", err)
		return
	}

	// Process a block.  For this example, we are going to intentionally
	// cause an error by trying to process the genesis block which already
	// exists.
	genesisBlock := btcutil.NewBlock(chaincfg.MainNetParams.GenesisBlock)
	isMainChain, isOrphan, err := chain.ProcessBlock(genesisBlock,
		blockchain.BFNone)
	if err != nil {
		fmt.Printf("Failed to process block: %v\n", err)
		return
	}
	fmt.Printf("Block accepted. Is it on the main chain?: %v", isMainChain)
	fmt.Printf("Block accepted. Is it an orphan?: %v", isOrphan)

	// Output:
	// Failed to process block: already have block 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
}
Esempio n. 3
0
// TestCheckBlockSanity tests the CheckBlockSanity function to ensure it works
// as expected.
func TestCheckBlockSanity(t *testing.T) {
	powLimit := chaincfg.MainNetParams.PowLimit
	block := btcutil.NewBlock(&Block100000)
	timeSource := blockchain.NewMedianTime()
	err := blockchain.CheckBlockSanity(block, powLimit, timeSource)
	if err != nil {
		t.Errorf("CheckBlockSanity: %v", err)
	}

	// Ensure a block that has a timestamp with a precision higher than one
	// second fails.
	timestamp := block.MsgBlock().Header.Timestamp
	block.MsgBlock().Header.Timestamp = timestamp.Add(time.Nanosecond)
	err = blockchain.CheckBlockSanity(block, powLimit, timeSource)
	if err == nil {
		t.Errorf("CheckBlockSanity: error is nil when it shouldn't be")
	}
}
Esempio n. 4
0
// TestMedianTime tests the medianTime implementation.
func TestMedianTime(t *testing.T) {
	tests := []struct {
		in         []int64
		wantOffset int64
		useDupID   bool
	}{
		// Not enough samples must result in an offset of 0.
		{in: []int64{1}, wantOffset: 0},
		{in: []int64{1, 2}, wantOffset: 0},
		{in: []int64{1, 2, 3}, wantOffset: 0},
		{in: []int64{1, 2, 3, 4}, wantOffset: 0},

		// Various number of entries.  The expected offset is only
		// updated on odd number of elements.
		{in: []int64{-13, 57, -4, -23, -12}, wantOffset: -12},
		{in: []int64{55, -13, 61, -52, 39, 55}, wantOffset: 39},
		{in: []int64{-62, -58, -30, -62, 51, -30, 15}, wantOffset: -30},
		{in: []int64{29, -47, 39, 54, 42, 41, 8, -33}, wantOffset: 39},
		{in: []int64{37, 54, 9, -21, -56, -36, 5, -11, -39}, wantOffset: -11},
		{in: []int64{57, -28, 25, -39, 9, 63, -16, 19, -60, 25}, wantOffset: 9},
		{in: []int64{-5, -4, -3, -2, -1}, wantOffset: -3, useDupID: true},

		// The offset stops being updated once the max number of entries
		// has been reached.  This is actually a bug from Bitcoin Core,
		// but since the time is ultimately used as a part of the
		// consensus rules, it must be mirrored.
		{in: []int64{-67, 67, -50, 24, 63, 17, 58, -14, 5, -32, -52}, wantOffset: 17},
		{in: []int64{-67, 67, -50, 24, 63, 17, 58, -14, 5, -32, -52, 45}, wantOffset: 17},
		{in: []int64{-67, 67, -50, 24, 63, 17, 58, -14, 5, -32, -52, 45, 4}, wantOffset: 17},

		// Offsets that are too far away from the local time should
		// be ignored.
		{in: []int64{-4201, 4202, -4203, 4204, -4205}, wantOffset: 0},

		// Excerise the condition where the median offset is greater
		// than the max allowed adjustment, but there is at least one
		// sample that is close enough to the current time to avoid
		// triggering a warning about an invalid local clock.
		{in: []int64{4201, 4202, 4203, 4204, -299}, wantOffset: 0},
	}

	// Modify the max number of allowed median time entries for these tests.
	blockchain.TstSetMaxMedianTimeEntries(10)
	defer blockchain.TstSetMaxMedianTimeEntries(200)

	for i, test := range tests {
		filter := blockchain.NewMedianTime()
		for j, offset := range test.in {
			id := strconv.Itoa(j)
			now := time.Unix(time.Now().Unix(), 0)
			tOffset := now.Add(time.Duration(offset) * time.Second)
			filter.AddTimeSample(id, tOffset)

			// Ensure the duplicate IDs are ignored.
			if test.useDupID {
				// Modify the offsets to ensure the final median
				// would be different if the duplicate is added.
				tOffset = tOffset.Add(time.Duration(offset) *
					time.Second)
				filter.AddTimeSample(id, tOffset)
			}
		}

		// Since it is possible that the time.Now call in AddTimeSample
		// and the time.Now calls here in the tests will be off by one
		// second, allow a fudge factor to compensate.
		gotOffset := filter.Offset()
		wantOffset := time.Duration(test.wantOffset) * time.Second
		wantOffset2 := time.Duration(test.wantOffset-1) * time.Second
		if gotOffset != wantOffset && gotOffset != wantOffset2 {
			t.Errorf("Offset #%d: unexpected offset -- got %v, "+
				"want %v or %v", i, gotOffset, wantOffset,
				wantOffset2)
			continue
		}

		// Since it is possible that the time.Now call in AdjustedTime
		// and the time.Now call here in the tests will be off by one
		// second, allow a fudge factor to compensate.
		adjustedTime := filter.AdjustedTime()
		now := time.Unix(time.Now().Unix(), 0)
		wantTime := now.Add(filter.Offset())
		wantTime2 := now.Add(filter.Offset() - time.Second)
		if !adjustedTime.Equal(wantTime) && !adjustedTime.Equal(wantTime2) {
			t.Errorf("AdjustedTime #%d: unexpected result -- got %v, "+
				"want %v or %v", i, adjustedTime, wantTime,
				wantTime2)
			continue
		}
	}
}
Esempio n. 5
0
// chainSetup is used to create a new db and chain instance with the genesis
// block already inserted.  In addition to the new chain instance, it returns
// a teardown function the caller should invoke when done testing to clean up.
func chainSetup(dbName string, params *chaincfg.Params) (*blockchain.BlockChain, func(), error) {
	if !isSupportedDbType(testDbType) {
		return nil, nil, fmt.Errorf("unsupported db type %v", testDbType)
	}

	// Handle memory database specially since it doesn't need the disk
	// specific handling.
	var db database.DB
	var teardown func()
	if testDbType == "memdb" {
		ndb, err := database.Create(testDbType)
		if err != nil {
			return nil, nil, fmt.Errorf("error creating db: %v", err)
		}
		db = ndb

		// Setup a teardown function for cleaning up.  This function is
		// returned to the caller to be invoked when it is done testing.
		teardown = func() {
			db.Close()
		}
	} else {
		// Create the root directory for test databases.
		if !fileExists(testDbRoot) {
			if err := os.MkdirAll(testDbRoot, 0700); err != nil {
				err := fmt.Errorf("unable to create test db "+
					"root: %v", err)
				return nil, nil, err
			}
		}

		// Create a new database to store the accepted blocks into.
		dbPath := filepath.Join(testDbRoot, dbName)
		_ = os.RemoveAll(dbPath)
		ndb, err := database.Create(testDbType, dbPath, blockDataNet)
		if err != nil {
			return nil, nil, fmt.Errorf("error creating db: %v", err)
		}
		db = ndb

		// Setup a teardown function for cleaning up.  This function is
		// returned to the caller to be invoked when it is done testing.
		teardown = func() {
			db.Close()
			os.RemoveAll(dbPath)
			os.RemoveAll(testDbRoot)
		}
	}

	// Copy the chain params to ensure any modifications the tests do to
	// the chain parameters do not affect the global instance.
	paramsCopy := *params

	// Create the main chain instance.
	chain, err := blockchain.New(&blockchain.Config{
		DB:          db,
		ChainParams: &paramsCopy,
		TimeSource:  blockchain.NewMedianTime(),
		SigCache:    txscript.NewSigCache(1000),
	})
	if err != nil {
		teardown()
		err := fmt.Errorf("failed to create chain instance: %v", err)
		return nil, nil, err
	}
	return chain, teardown, nil
}