Exemplo n.º 1
0
// submitBlock submits the passed block to network after ensuring it passes all
// of the consensus validation rules.
func (m *CPUMiner) submitBlock(block *dcrutil.Block) bool {
	m.submitBlockLock.Lock()
	defer m.submitBlockLock.Unlock()

	_, latestHeight := m.server.blockManager.chainState.Best()

	// Be sure to set this so ProcessBlock doesn't fail! - Decred
	block.SetHeight(latestHeight + 1)

	// Process this block using the same rules as blocks coming from other
	// nodes. This will in turn relay it to the network like normal.
	isOrphan, err := m.server.blockManager.ProcessBlock(block, blockchain.BFNone)
	if err != nil {
		// Anything other than a rule violation is an unexpected error,
		// so log that error as an internal error.
		if rErr, ok := err.(blockchain.RuleError); !ok {
			minrLog.Errorf("Unexpected error while processing "+
				"block submitted via CPU miner: %v", err)
			return false
		} else {
			// Occasionally errors are given out for timing errors with
			// ResetMinDifficulty and high block works that is above
			// the target. Feed these to debug.
			if m.server.chainParams.ResetMinDifficulty &&
				rErr.ErrorCode == blockchain.ErrHighHash {
				minrLog.Debugf("Block submitted via CPU miner rejected "+
					"because of ResetMinDifficulty time sync failure: %v",
					err)
				return false
			} else {
				// Other rule errors should be reported.
				minrLog.Errorf("Block submitted via CPU miner rejected: %v", err)
				return false
			}
		}

	}
	if isOrphan {
		minrLog.Errorf("Block submitted via CPU miner is an orphan building "+
			"on parent %v", block.MsgBlock().Header.PrevBlock)
		return false
	}

	// The block was accepted.
	coinbaseTxOuts := block.MsgBlock().Transactions[0].TxOut
	coinbaseTxGenerated := int64(0)
	for _, out := range coinbaseTxOuts {
		coinbaseTxGenerated += out.Value
	}
	minrLog.Infof("Block submitted via CPU miner accepted (hash %s, "+
		"height %v, amount %v)",
		block.Sha(),
		block.Height(),
		dcrutil.Amount(coinbaseTxGenerated))
	return true
}
Exemplo n.º 2
0
// ticketsRevokedInBlock fetches a list of tickets that were revoked in the
// block.
func ticketsRevokedInBlock(bl *dcrutil.Block) []chainhash.Hash {
	var tickets []chainhash.Hash
	for _, stx := range bl.MsgBlock().STransactions {
		if stake.DetermineTxType(stx) == stake.TxTypeSSRtx {
			tickets = append(tickets, stx.TxIn[0].PreviousOutPoint.Hash)
		}
	}

	return tickets
}
Exemplo n.º 3
0
// ticketsSpentInBlock finds all the tickets spent in the block.
func ticketsSpentInBlock(bl *dcrutil.Block) []chainhash.Hash {
	tickets := make([]chainhash.Hash, 0)
	for _, stx := range bl.STransactions() {
		if DetermineTxType(stx.MsgTx()) == TxTypeSSGen {
			tickets = append(tickets, stx.MsgTx().TxIn[1].PreviousOutPoint.Hash)
		}
	}

	return tickets
}
Exemplo n.º 4
0
// voteVersionsInBlock returns all versions in a block.
func voteVersionsInBlock(bl *dcrutil.Block, params *chaincfg.Params) []uint32 {
	versions := make([]uint32, 0, params.TicketsPerBlock)
	for _, stx := range bl.MsgBlock().STransactions {
		if is, _ := stake.IsSSGen(stx); !is {
			continue
		}
		versions = append(versions, stake.SSGenVersion(stx))
	}

	return versions
}
Exemplo n.º 5
0
// ticketsInBlock finds all the new tickets in the block.
func ticketsInBlock(bl *dcrutil.Block) []chainhash.Hash {
	tickets := make([]chainhash.Hash, 0)
	for _, stx := range bl.STransactions() {
		if DetermineTxType(stx.MsgTx()) == TxTypeSStx {
			h := stx.Sha()
			tickets = append(tickets, *h)
		}
	}

	return tickets
}
Exemplo n.º 6
0
// unspendStakeTxTree returns all outpoints spent before this one
// in the block's tx tree stake. used for unspending the stake tx
// tree to evaluate tx tree regular of prev block.
func unspendStakeTxTree(block *dcrutil.Block) map[wire.OutPoint]struct{} {
	unspentOps := make(map[wire.OutPoint]struct{})

	for _, tx := range block.STransactions() {
		for _, txIn := range tx.MsgTx().TxIn {
			unspentOps[txIn.PreviousOutPoint] = struct{}{}
		}
	}

	return unspentOps
}
Exemplo n.º 7
0
// votesInBlock finds all the votes in the block.
func votesInBlock(bl *dcrutil.Block) []chainhash.Hash {
	votes := make([]chainhash.Hash, 0)
	for _, stx := range bl.STransactions() {
		if DetermineTxType(stx.MsgTx()) == TxTypeSSGen {
			h := stx.Sha()
			votes = append(votes, *h)
		}
	}

	return votes
}
Exemplo n.º 8
0
// dbIndexDisconnectBlock removes all of the index entries associated with the
// given block using the provided indexer and updates the tip of the indexer
// accordingly.  An error will be returned if the current tip for the indexer is
// not the passed block.
func dbIndexDisconnectBlock(dbTx database.Tx, indexer Indexer, block, parent *dcrutil.Block, view *blockchain.UtxoViewpoint) error {
	// Assert that the block being disconnected is the current tip of the
	// index.
	idxKey := indexer.Key()
	curTipHash, _, err := dbFetchIndexerTip(dbTx, idxKey)
	if err != nil {
		return err
	}
	if !curTipHash.IsEqual(block.Sha()) {
		return AssertError(fmt.Sprintf("dbIndexDisconnectBlock must "+
			"be called with the block at the current index tip "+
			"(%s, tip %s, block %s)", indexer.Name(),
			curTipHash, block.Sha()))
	}

	// Notify the indexer with the disconnected block so it can remove all
	// of the appropriate entries.
	if err := indexer.DisconnectBlock(dbTx, block, parent, view); err != nil {
		return err
	}

	// Update the current index tip.
	prevHash := &block.MsgBlock().Header.PrevBlock
	return dbPutIndexerTip(dbTx, idxKey, prevHash, uint32(block.Height())-1)
}
Exemplo n.º 9
0
// checkBlockContext peforms several validation checks on the block which depend
// on its position within the block chain.
//
// The flags modify the behavior of this function as follows:
//  - BFFastAdd: The transaction are not checked to see if they are finalized
//    and the somewhat expensive duplication transaction check is not performed.
//
// The flags are also passed to checkBlockHeaderContext.  See its documentation
// for how the flags modify its behavior.
func (b *BlockChain) checkBlockContext(block *dcrutil.Block, prevNode *blockNode,
	flags BehaviorFlags) error {
	// The genesis block is valid by definition.
	if prevNode == nil {
		return nil
	}

	// Perform all block header related validation checks.
	header := &block.MsgBlock().Header
	err := b.checkBlockHeaderContext(header, prevNode, flags)
	if err != nil {
		return err
	}

	fastAdd := flags&BFFastAdd == BFFastAdd
	if !fastAdd {
		// The height of this block is one more than the referenced
		// previous block.
		blockHeight := prevNode.height + 1

		// Ensure all transactions in the block are finalized.
		for _, tx := range block.Transactions() {
			if !IsFinalizedTransaction(tx, blockHeight,
				header.Timestamp) {

				str := fmt.Sprintf("block contains unfinalized regular "+
					"transaction %v", tx.Sha())
				return ruleError(ErrUnfinalizedTx, str)
			}
		}
		for _, stx := range block.STransactions() {
			if !IsFinalizedTransaction(stx, blockHeight,
				header.Timestamp) {

				str := fmt.Sprintf("block contains unfinalized stake "+
					"transaction %v", stx.Sha())
				return ruleError(ErrUnfinalizedTx, str)
			}
		}

		// Check that the node is at the correct height in the blockchain,
		// as specified in the block header.
		if blockHeight != int64(block.MsgBlock().Header.Height) {
			errStr := fmt.Sprintf("Block header height invalid; expected %v"+
				" but %v was found", blockHeight, header.Height)
			return ruleError(ErrBadBlockHeight, errStr)
		}

		// Check that the coinbase contains at minimum the block
		// height in output 1.
		if blockHeight > 1 {
			err := checkCoinbaseUniqueHeight(blockHeight, block)
			if err != nil {
				return err
			}
		}
	}

	return nil
}
Exemplo n.º 10
0
// SubmitBlockAsync returns an instance of a type that can be used to get the
// result of the RPC at some future time by invoking the Receive function on the
// returned instance.
//
// See SubmitBlock for the blocking version and more details.
func (c *Client) SubmitBlockAsync(block *dcrutil.Block, options *dcrjson.SubmitBlockOptions) FutureSubmitBlockResult {
	blockHex := ""
	if block != nil {
		blockBytes, err := block.Bytes()
		if err != nil {
			return newFutureError(err)
		}

		blockHex = hex.EncodeToString(blockBytes)
	}

	cmd := dcrjson.NewSubmitBlockCmd(blockHex, options)
	return c.sendCmd(cmd)
}
Exemplo n.º 11
0
// InsertBlock synchronously queues a newly solved block to have its
// transactions indexed by address.
func (a *addrIndexer) InsertBlock(block *dcrutil.Block, parent *dcrutil.Block) error {
	addrIndex, err := a.indexBlockAddrs(block, parent)
	if err != nil {
		return fmt.Errorf("Unable to index transactions of"+
			" block: %v", err)
	}
	err = a.server.db.UpdateAddrIndexForBlock(block.Sha(),
		block.Height(),
		addrIndex)
	if err != nil {
		return fmt.Errorf("Unable to insert block: %v", err.Error())
	}

	return nil
}
Exemplo n.º 12
0
// DisconnectBlock is invoked by the index manager when a block has been
// disconnected from the main chain.  This indexer removes the
// hash-to-transaction mapping for every transaction in the block.
//
// This is part of the Indexer interface.
func (idx *TxIndex) DisconnectBlock(dbTx database.Tx, block, parent *dcrutil.Block, view *blockchain.UtxoViewpoint) error {
	// Remove all of the transactions in the block from the index.
	if err := dbRemoveTxIndexEntries(dbTx, block, parent); err != nil {
		return err
	}

	// Remove the block ID index entry for the block being disconnected and
	// decrement the current internal block ID to account for it.
	blockSha := block.Sha()
	if err := dbRemoveBlockIDIndexEntry(dbTx, *blockSha); err != nil {
		return err
	}
	idx.curBlockID--
	return nil
}
Exemplo n.º 13
0
// unspendInflightTxTree returns all outpoints spent that reference internal
// transactions in a TxTreeRegular.
func unspendInflightTxTree(block *dcrutil.Block) map[wire.OutPoint]struct{} {
	unspentOps := make(map[wire.OutPoint]struct{})
	allTxHashes := make(map[chainhash.Hash]struct{})
	for _, tx := range block.Transactions() {
		h := tx.Sha()
		allTxHashes[*h] = struct{}{}
	}

	for _, tx := range block.Transactions() {
		for _, txIn := range tx.MsgTx().TxIn {
			if _, isLocal := allTxHashes[txIn.PreviousOutPoint.Hash]; isLocal {
				unspentOps[txIn.PreviousOutPoint] = struct{}{}
			}
		}
	}

	return unspentOps
}
Exemplo n.º 14
0
// ConnectBlock is invoked by the index manager when a new block has been
// connected to the main chain.  This indexer adds a hash-to-transaction mapping
// for every transaction in the passed block.
//
// This is part of the Indexer interface.
func (idx *TxIndex) ConnectBlock(dbTx database.Tx, block, parent *dcrutil.Block, view *blockchain.UtxoViewpoint) error {
	// Increment the internal block ID to use for the block being connected
	// and add all of the transactions in the block to the index.
	newBlockID := idx.curBlockID + 1
	if err := dbAddTxIndexEntries(dbTx, block, parent, newBlockID); err != nil {
		return err
	}

	// Add the new block ID index entry for the block being connected and
	// update the current internal block ID accordingly.
	blockSha := block.Sha()
	err := dbPutBlockIDIndexEntry(dbTx, *blockSha, newBlockID)
	if err != nil {
		return err
	}
	idx.curBlockID = newBlockID
	return nil
}
Exemplo n.º 15
0
// checkBlockScripts executes and validates the scripts for all transactions in
// the passed block using multiple goroutines.
// txTree = true is TxTreeRegular, txTree = false is TxTreeStake.
func checkBlockScripts(block *dcrutil.Block, utxoView *UtxoViewpoint, txTree bool,
	scriptFlags txscript.ScriptFlags, sigCache *txscript.SigCache) error {

	// Collect all of the transaction inputs and required information for
	// validation for all transactions in the block into a single slice.
	numInputs := 0
	var txs []*dcrutil.Tx

	// TxTreeRegular handling.
	if txTree {
		txs = block.Transactions()
	} else { // TxTreeStake
		txs = block.STransactions()
	}

	for _, tx := range txs {
		numInputs += len(tx.MsgTx().TxIn)
	}
	txValItems := make([]*txValidateItem, 0, numInputs)
	for _, tx := range txs {
		for txInIdx, txIn := range tx.MsgTx().TxIn {
			// Skip coinbases.
			if txIn.PreviousOutPoint.Index == math.MaxUint32 {
				continue
			}

			txVI := &txValidateItem{
				txInIndex: txInIdx,
				txIn:      txIn,
				tx:        tx,
			}
			txValItems = append(txValItems, txVI)
		}
	}

	// Validate all of the inputs.
	validator := newTxValidator(utxoView, scriptFlags, sigCache)
	if err := validator.Validate(txValItems); err != nil {
		return err
	}

	return nil
}
Exemplo n.º 16
0
// getRegTreeOpsSpentBeforeThisOp returns all outpoints spent before this one
// in the block's tx tree regular. used for checking vs in flight tx.
func getRegTreeOpsSpentBeforeThisOp(block *dcrutil.Block, idx int, txinIdx int) map[wire.OutPoint]struct{} {
	spentOps := make(map[wire.OutPoint]struct{})

	thisTx := block.Transactions()[idx]
	for i, txIn := range thisTx.MsgTx().TxIn {
		if i < txinIdx {
			spentOps[txIn.PreviousOutPoint] = struct{}{}
		}
	}

	for i, tx := range block.Transactions() {
		if i < idx {
			for _, txIn := range tx.MsgTx().TxIn {
				spentOps[txIn.PreviousOutPoint] = struct{}{}
			}
		}
	}

	return spentOps
}
Exemplo n.º 17
0
// testExistsTxSha ensures ExistsTxSha conforms to the interface contract.
func testExistsTxSha(tc *testContext) bool {
	var blockPrev *dcrutil.Block = nil
	// Decred: WARNING. This function assumes that all block insertion calls have
	// dcrutil.blocks passed to them with block.blockHeight set correctly. However,
	// loading the genesis block in dcrd didn't do this (via block manager); pre-
	// production it should be established that all calls to this function pass
	// blocks with block.blockHeight set correctly.
	if tc.block.Height() != 0 {
		var errBlockPrev error
		blockPrev, errBlockPrev = tc.db.FetchBlockBySha(&tc.block.MsgBlock().Header.PrevBlock)
		if errBlockPrev != nil {
			blockSha := tc.block.Sha()
			tc.t.Errorf("Failed to fetch parent block of block %v", blockSha)
		}
	}

	votebits := tc.block.MsgBlock().Header.VoteBits
	if dcrutil.IsFlagSet16(votebits, dcrutil.BlockValid) && blockPrev != nil {
		for i, tx := range blockPrev.Transactions() {
			// The transaction must exist in the database.
			txHash := tx.Sha()
			exists, err := tc.db.ExistsTxSha(txHash)
			if err != nil {
				tc.t.Errorf("ExistsTxSha (%s): block #%d (%s) tx #%d "+
					"(%s) unexpected error: %v", tc.dbType,
					tc.blockHeight, tc.blockHash, i, txHash, err)
				return false
			}
			if !exists {
				_, err := tc.db.FetchTxBySha(txHash)
				if err != nil {
					tc.t.Errorf("ExistsTxSha (%s): block #%d (%s) "+
						"tx #%d (%s) does not exist", tc.dbType,
						tc.blockHeight, tc.blockHash, i, txHash)
				}
				return false
			}
		}
	}
	return true
}
Exemplo n.º 18
0
// LogBlockHeight logs a new block height as an information message to show
// progress to the user. In order to prevent spam, it limits logging to one
// message every 10 seconds with duration and totals included.
func (b *blockProgressLogger) LogBlockHeight(block *dcrutil.Block) {
	b.Lock()
	defer b.Unlock()

	b.receivedLogBlocks++
	b.receivedLogTx += int64(len(block.MsgBlock().Transactions))

	now := time.Now()
	duration := now.Sub(b.lastBlockLogTime)
	if duration < time.Second*10 {
		return
	}

	// Truncate the duration to 10s of milliseconds.
	durationMillis := int64(duration / time.Millisecond)
	tDuration := 10 * time.Millisecond * time.Duration(durationMillis/10)

	// Log information about new block height.
	blockStr := "blocks"
	if b.receivedLogBlocks == 1 {
		blockStr = "block"
	}
	txStr := "transactions"
	if b.receivedLogTx == 1 {
		txStr = "transaction"
	}
	b.subsystemLogger.Infof("%s %d %s in the last %s (%d %s, height %d, %s)",
		b.progressAction, b.receivedLogBlocks, blockStr, tDuration, b.receivedLogTx,
		txStr, block.Height(), block.MsgBlock().Header.Timestamp)

	b.receivedLogBlocks = 0
	b.receivedLogTx = 0
	b.lastBlockLogTime = now
}
Exemplo n.º 19
0
// loadReorgBlocks reads files containing decred block data (bzipped but
// otherwise in the format bitcoind writes) from disk and returns them as an
// array of dcrutil.Block. This is copied from the blockchain package, which
// itself largely borrowed it from the test code in this package.
func loadReorgBlocks(filename string) ([]*dcrutil.Block, error) {
	filename = filepath.Join("../blockchain/testdata/", filename)
	fi, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	bcStream := bzip2.NewReader(fi)
	defer fi.Close()

	// Create a buffer of the read file
	bcBuf := new(bytes.Buffer)
	bcBuf.ReadFrom(bcStream)

	// Create decoder from the buffer and a map to store the data
	bcDecoder := gob.NewDecoder(bcBuf)
	blockchain := make(map[int64][]byte)

	// Decode the blockchain into the map
	if err := bcDecoder.Decode(&blockchain); err != nil {
		return nil, err
	}

	var block *dcrutil.Block

	blocks := make([]*dcrutil.Block, 0, len(blockchain))
	for height := int64(0); height < int64(len(blockchain)); height++ {
		block, err = dcrutil.NewBlockFromBytes(blockchain[height])
		if err != nil {
			return blocks, err
		}
		block.SetHeight(height)
		blocks = append(blocks, block)
	}

	return blocks, nil
}
Exemplo n.º 20
0
// dbIndexConnectBlock adds all of the index entries associated with the
// given block using the provided indexer and updates the tip of the indexer
// accordingly.  An error will be returned if the current tip for the indexer is
// not the previous block for the passed block.
func dbIndexConnectBlock(dbTx database.Tx, indexer Indexer, block, parent *dcrutil.Block, view *blockchain.UtxoViewpoint) error {
	// Assert that the block being connected properly connects to the
	// current tip of the index.
	idxKey := indexer.Key()
	curTipHash, _, err := dbFetchIndexerTip(dbTx, idxKey)
	if err != nil {
		return err
	}
	if !curTipHash.IsEqual(&block.MsgBlock().Header.PrevBlock) {
		return AssertError(fmt.Sprintf("dbIndexConnectBlock must be "+
			"called with a block that extends the current index "+
			"tip (%s, tip %s, block %s)", indexer.Name(),
			curTipHash, block.Sha()))
	}

	// Notify the indexer with the connected block so it can index it.
	if err := indexer.ConnectBlock(dbTx, block, parent, view); err != nil {
		return err
	}

	// Update the current index tip.
	return dbPutIndexerTip(dbTx, idxKey, block.Sha(), uint32(block.Height()))
}
Exemplo n.º 21
0
// CalculateAddedSubsidy calculates the amount of subsidy added by a block
// and its parent. The blocks passed to this function MUST be valid blocks
// that have already been confirmed to abide by the consensus rules of the
// network, or the function might panic.
func CalculateAddedSubsidy(block, parent *dcrutil.Block) int64 {
	var subsidy int64

	regularTxTreeValid := dcrutil.IsFlagSet16(block.MsgBlock().Header.VoteBits,
		dcrutil.BlockValid)
	if regularTxTreeValid {
		subsidy += parent.MsgBlock().Transactions[0].TxIn[0].ValueIn
	}

	for _, stx := range block.MsgBlock().STransactions {
		if isSSGen, _ := stake.IsSSGen(stx); isSSGen {
			subsidy += stx.TxIn[0].ValueIn
		}
	}

	return subsidy
}
Exemplo n.º 22
0
// NewMerkleBlock returns a new *wire.MsgMerkleBlock and an array of the matched
// transaction hashes based on the passed block and filter.
func NewMerkleBlock(block *dcrutil.Block, filter *Filter) (*wire.MsgMerkleBlock, []*chainhash.Hash) {
	numTx := uint32(len(block.Transactions()))
	mBlock := merkleBlock{
		numTx:       numTx,
		allHashes:   make([]*chainhash.Hash, 0, numTx),
		matchedBits: make([]byte, 0, numTx),
	}

	// Find and keep track of any transactions that match the filter.
	var matchedHashes []*chainhash.Hash
	for _, tx := range block.Transactions() {
		if filter.MatchTxAndUpdate(tx) {
			mBlock.matchedBits = append(mBlock.matchedBits, 0x01)
			matchedHashes = append(matchedHashes, tx.Sha())
		} else {
			mBlock.matchedBits = append(mBlock.matchedBits, 0x00)
		}
		mBlock.allHashes = append(mBlock.allHashes, tx.Sha())
	}

	// Calculate the number of merkle branches (height) in the tree.
	height := uint32(0)
	for mBlock.calcTreeWidth(height) > 1 {
		height++
	}

	// Build the depth-first partial merkle tree.
	mBlock.traverseAndBuild(height, 0)

	// Create and return the merkle block.
	msgMerkleBlock := wire.MsgMerkleBlock{
		Header:       block.MsgBlock().Header,
		Transactions: uint32(mBlock.numTx),
		Hashes:       make([]*chainhash.Hash, 0, len(mBlock.finalHashes)),
		Flags:        make([]byte, (len(mBlock.bits)+7)/8),
	}
	for _, sha := range mBlock.finalHashes {
		msgMerkleBlock.AddTxHash(sha)
	}
	for i := uint32(0); i < uint32(len(mBlock.bits)); i++ {
		msgMerkleBlock.Flags[i/8] |= mBlock.bits[i] << (i % 8)
	}
	return &msgMerkleBlock, matchedHashes
}
Exemplo n.º 23
0
// connectTxTree lets you connect an arbitrary TxTree to a txStore to push it
// forward in history.
// TxTree true == TxTreeRegular
// TxTree false == TxTreeStake
func connectTxTree(txStore TxStore,
	block *dcrutil.Block,
	txTree bool) {
	var transactions []*dcrutil.Tx
	if txTree {
		transactions = block.Transactions()
	} else {
		transactions = block.STransactions()
	}

	// Loop through all of the transactions in the block to see if any of
	// them are ones we need to update and spend based on the results map.
	for i, tx := range transactions {
		// Update the transaction store with the transaction information
		// if it's one of the requested transactions.
		msgTx := tx.MsgTx()
		if txD, exists := txStore[*tx.Sha()]; exists {
			txD.Tx = tx
			txD.BlockHeight = block.Height()
			txD.BlockIndex = uint32(i)
			txD.Spent = make([]bool, len(msgTx.TxOut))
			txD.Err = nil
		}

		// Spend the origin transaction output.
		for _, txIn := range msgTx.TxIn {
			originHash := &txIn.PreviousOutPoint.Hash
			originIndex := txIn.PreviousOutPoint.Index
			if originTx, exists := txStore[*originHash]; exists {
				if originTx.Spent == nil {
					continue
				}
				if originIndex >= uint32(len(originTx.Spent)) {
					continue
				}
				originTx.Spent[originIndex] = true
			}
		}
	}

	return
}
Exemplo n.º 24
0
// dbRemoveTxIndexEntries uses an existing database transaction to remove the
// latest transaction entry for every transaction in the passed block.
func dbRemoveTxIndexEntries(dbTx database.Tx, block, parent *dcrutil.Block) error {
	regularTxTreeValid := dcrutil.IsFlagSet16(block.MsgBlock().Header.VoteBits,
		dcrutil.BlockValid)
	if regularTxTreeValid {
		for _, tx := range parent.Transactions() {
			txSha := tx.Sha()
			err := dbRemoveTxIndexEntry(dbTx, *txSha)
			if err != nil {
				return err
			}
		}
	}
	for _, tx := range block.STransactions() {
		txSha := tx.Sha()
		err := dbRemoveTxIndexEntry(dbTx, *txSha)
		if err != nil {
			return err
		}
	}

	return nil
}
Exemplo n.º 25
0
// InsertBlock inserts raw block and transaction data from a block into the
// database.  The first block inserted into the database will be treated as the
// genesis block.  Every subsequent block insert requires the referenced parent
// block to already exist.  This is part of the database.Db interface
// implementation.
func (db *MemDb) InsertBlock(block *dcrutil.Block) (int64, error) {
	db.Lock()
	defer db.Unlock()

	if db.closed {
		return 0, ErrDbClosed
	}

	// Reject the insert if the previously reference block does not exist
	// except in the case there are no blocks inserted yet where the first
	// inserted block is assumed to be a genesis block.
	msgBlock := block.MsgBlock()
	if _, exists := db.blocksBySha[msgBlock.Header.PrevBlock]; !exists {
		if len(db.blocks) > 0 {
			return 0, database.ErrPrevShaMissing
		}
	}
	var blockPrev *dcrutil.Block = nil
	// Decred: WARNING. This function assumes that all block insertion calls have
	// dcrutil.blocks passed to them with block.blockHeight set correctly. However,
	// loading the genesis block in dcrd didn't do this (via block manager); pre-
	// production it should be established that all calls to this function pass
	// blocks with block.blockHeight set correctly.
	if len(db.blocks) > 0 {
		var errBlockPrev error
		blockPrev, errBlockPrev = db.fetchBlockBySha(&msgBlock.Header.PrevBlock)
		if errBlockPrev != nil {
			blockSha := block.Sha()
			log.Warnf("Failed to fetch parent block of block %v", blockSha)
			return 0, errBlockPrev
		}
	}

	// Build a map of in-flight transactions because some of the inputs in
	// this block could be referencing other transactions earlier in this
	// block which are not yet in the chain.
	newHeight := int64(len(db.blocks))
	txInFlight := map[chainhash.Hash]int{}
	// Loop through all transactions and inputs to ensure there are no error
	// conditions that would prevent them from be inserted into the db.
	// Although these checks could could be done in the loop below, checking
	// for error conditions up front means the code below doesn't have to
	// deal with rollback on errors.
	votebits := block.MsgBlock().Header.VoteBits
	if dcrutil.IsFlagSet16(votebits, dcrutil.BlockValid) && blockPrev != nil {
		transactions := blockPrev.Transactions()
		for i, tx := range transactions {
			txInFlight[*tx.Sha()] = i
		}
		for i, tx := range transactions {
			for _, txIn := range tx.MsgTx().TxIn {
				if isCoinbaseInput(txIn) {
					continue
				}

				// It is acceptable for a transaction input to reference
				// the output of another transaction in this block only
				// if the referenced transaction comes before the
				// current one in this block.
				prevOut := &txIn.PreviousOutPoint
				if inFlightIndex, ok := txInFlight[prevOut.Hash]; ok {
					if i <= inFlightIndex {
						log.Warnf("InsertBlock: requested hash "+
							" of %s does not exist in-flight",
							tx.Sha())
						return 0, database.ErrTxShaMissing
					}
				} else {
					originTxns, exists := db.txns[prevOut.Hash]
					if !exists {
						log.Warnf("InsertBlock: requested hash "+
							"of %s by %s does not exist",
							prevOut.Hash, tx.Sha())
						return 0, database.ErrTxShaMissing
					}
					originTxD := originTxns[len(originTxns)-1]
					if prevOut.Index > uint32(len(originTxD.spentBuf)) {
						log.Warnf("InsertBlock: requested hash "+
							"of %s with index %d does not "+
							"exist", tx.Sha(), prevOut.Index)
						return 0, database.ErrTxShaMissing
					}
				}
			}

			// Prevent duplicate transactions in the same block.
			if inFlightIndex, exists := txInFlight[*tx.Sha()]; exists &&
				inFlightIndex < i {
				log.Warnf("Block contains duplicate transaction %s",
					tx.Sha())
				return 0, database.ErrDuplicateSha
			}

			// Prevent duplicate transactions unless the old one is fully
			// spent.
			if txns, exists := db.txns[*tx.Sha()]; exists {
				txD := txns[len(txns)-1]
				if !isFullySpent(txD) {
					log.Warnf("Attempt to insert duplicate "+
						"transaction %s", tx.Sha())
					return 0, database.ErrDuplicateSha
				}
			}
		}
	}

	db.blocks = append(db.blocks, msgBlock)
	db.blocksBySha[msgBlock.Header.BlockSha()] = newHeight
	if dcrutil.IsFlagSet16(votebits, dcrutil.BlockValid) && blockPrev != nil {
		// Insert information about eacj transaction and spend all of the
		// outputs referenced by the inputs to the transactions.
		for i, tx := range blockPrev.Transactions() {
			// Insert the transaction data.
			txD := tTxInsertData{
				tree:        dcrutil.TxTreeRegular,
				blockHeight: newHeight - 1,
				offset:      i,
				spentBuf:    make([]bool, len(tx.MsgTx().TxOut)),
			}
			db.txns[*tx.Sha()] = append(db.txns[*tx.Sha()], &txD)
			// Spend all of the inputs.
			for _, txIn := range tx.MsgTx().TxIn {
				// Coinbase transaction has no inputs.
				if isCoinbaseInput(txIn) {
					continue
				}

				// Already checked for existing and valid ranges above.
				prevOut := &txIn.PreviousOutPoint
				originTxns := db.txns[prevOut.Hash]
				originTxD := originTxns[len(originTxns)-1]
				originTxD.spentBuf[prevOut.Index] = true
			}
		}
	}
	for i, tx := range block.STransactions() {
		// Insert the transaction data.
		txD := tTxInsertData{
			tree:        dcrutil.TxTreeStake,
			blockHeight: newHeight,
			offset:      i,
			spentBuf:    make([]bool, len(tx.MsgTx().TxOut)),
		}
		db.txns[*tx.Sha()] = append(db.txns[*tx.Sha()], &txD)

		// Spend all of the inputs.
		for _, txIn := range tx.MsgTx().TxIn {
			// Coinbase transaction has no inputs.
			if isCoinbaseInput(txIn) {
				continue
			}

			// Already checked for existing and valid ranges above.
			prevOut := &txIn.PreviousOutPoint
			originTxns := db.txns[prevOut.Hash]
			originTxD := originTxns[len(originTxns)-1]
			originTxD.spentBuf[prevOut.Index] = true
		}

	}
	return newHeight, nil
}
Exemplo n.º 26
0
// spendTickets transfers tickets from the ticketMap to the spentTicketMap. Useful
// when connecting blocks. Also pushes missed tickets to the missed ticket map.
// usedtickets is a map that contains all tickets that were actually used in SSGen
// votes; all other tickets are considered missed.
//
// This function MUST be called with the tmdb lock held (for writes).
func (tmdb *TicketDB) spendTickets(parentBlock *dcrutil.Block,
	usedTickets map[chainhash.Hash]struct{},
	spendingHashes map[chainhash.Hash]chainhash.Hash) (SStxMemMap, error) {

	// If there is nothing being spent, break.
	if len(spendingHashes) < 1 {
		return nil, nil
	}

	// Make sure there's a bucket in the map for used tickets
	height := parentBlock.Height() + 1
	tmdb.maybeInsertBlock(height)

	tempTickets := make(SStxMemMap)

	// Sort the entire list of tickets lexicographically by sorting
	// each bucket and then appending it to the list.
	totalTickets := 0
	var sortedSlice []*TicketData
	for i := 0; i < BucketsSize; i++ {
		mapLen := len(tmdb.maps.ticketMap[i])
		totalTickets += mapLen
		tempTdSlice := NewTicketDataSlice(mapLen)
		itr := 0 // Iterator
		for _, td := range tmdb.maps.ticketMap[i] {
			tempTdSlice[itr] = td
			itr++
		}
		sort.Sort(tempTdSlice)
		sortedSlice = append(sortedSlice, tempTdSlice...)
	}

	// Use the parent block's header to seed a PRNG that picks the lottery winners.
	ticketsPerBlock := int(tmdb.chainParams.TicketsPerBlock)
	pbhB, err := parentBlock.MsgBlock().Header.Bytes()
	if err != nil {
		return nil, err
	}
	prng := NewHash256PRNG(pbhB)
	ts, err := FindTicketIdxs(int64(totalTickets), ticketsPerBlock, prng)
	if err != nil {
		return nil, err
	}
	ticketsToSpendOrMiss := make([]*TicketData, ticketsPerBlock, ticketsPerBlock)
	for i, idx := range ts {
		ticketsToSpendOrMiss[i] = sortedSlice[idx]
	}

	// Spend or miss these tickets by checking for their existence in the
	// passed usedtickets map.
	tixSpent := 0
	tixMissed := 0
	for _, ticket := range ticketsToSpendOrMiss {
		// Move the ticket from active tickets map into the used tickets map
		// if the ticket was spent.
		_, wasSpent := usedTickets[ticket.SStxHash]

		if wasSpent {
			ticket.Missed = false
			ticket.SpendHash = spendingHashes[ticket.SStxHash]
			err := tmdb.pushSpentTicket(height, ticket)
			if err != nil {
				return nil, err
			}
			err = tmdb.removeLiveTicket(ticket)
			if err != nil {
				return nil, err
			}
			tixSpent++
		} else { // Ticket missed being spent and --> false or nil
			ticket.Missed = true // TODO fix test failure @ L150 due to this
			err := tmdb.pushSpentTicket(height, ticket)
			if err != nil {
				return nil, err
			}
			err = tmdb.pushMissedTicket(ticket)
			if err != nil {
				return nil, err
			}
			err = tmdb.removeLiveTicket(ticket)
			if err != nil {
				return nil, err
			}
			tixMissed++
		}

		// Report on the spent and missed tickets for the block in debug.
		if ticket.Missed {
			log.Debugf("Ticket %v has been missed and expired from "+
				"the lottery pool as a missed ticket", ticket.SStxHash)
		} else {
			log.Debugf("Ticket %v was spent and removed from "+
				"the lottery pool", ticket.SStxHash)
		}

		// Add the ticket to the temporary tickets buffer for later use in
		// map restoration if needed.
		tempTickets[ticket.SStxHash] = ticket
	}

	// Some sanity checks.
	if tixSpent != len(usedTickets) {
		errStr := fmt.Sprintf("spendTickets error, an invalid number %v "+
			"tickets was spent, but %v many tickets should "+
			"have been spent!", tixSpent, len(usedTickets))
		return nil, errors.New(errStr)
	}

	if tixMissed != (ticketsPerBlock - len(usedTickets)) {
		errStr := fmt.Sprintf("spendTickets error, an invalid number %v "+
			"tickets was missed, but %v many tickets should "+
			"have been missed!", tixMissed, ticketsPerBlock-len(usedTickets))
		return nil, errors.New(errStr)
	}

	if (tixSpent + tixMissed) != ticketsPerBlock {
		errStr := fmt.Sprintf("spendTickets error, an invalid number %v "+
			"tickets was spent and missed, but TicketsPerBlock %v many "+
			"tickets should have been spent!", tixSpent, ticketsPerBlock)
		return nil, errors.New(errStr)
	}

	return tempTickets, nil
}
Exemplo n.º 27
0
// insertBlock is the internal function which implements the public
// InsertBlock.  See the comment for InsertBlock for more details.
//
// This function MUST be called with the tmdb lock held (for writes).
func (tmdb *TicketDB) insertBlock(block *dcrutil.Block) (SStxMemMap,
	SStxMemMap, SStxMemMap, error) {

	height := block.Height()
	if height < tmdb.StakeEnabledHeight {
		return nil, nil, nil, nil
	}

	// Sanity check: Does the number of tickets in ticketMap equal the number
	// of tickets indicated in the header?
	poolSizeBlock := int(block.MsgBlock().Header.PoolSize)
	poolSize := 0
	for i := 0; i < BucketsSize; i++ {
		poolSize += len(tmdb.maps.ticketMap[i])
	}
	if poolSize != poolSizeBlock {
		return nil, nil, nil, fmt.Errorf("ticketpoolsize in block %v not "+
			"equal to the calculated ticketpoolsize, indicating database "+
			"corruption (got %v, want %v)",
			block.Sha(),
			poolSizeBlock,
			poolSize)
	}

	// Create the block in the spentTicketMap.
	tmdb.maybeInsertBlock(block.Height())

	// Iterate through all the SSGen (vote) tx in the block and add them to
	// a map of tickets that were actually used. The rest of the tickets in
	// the buckets were then considered missed --> missedTicketMap.
	// Note that it doesn't really matter what value you set usedTickets to,
	// it's just a map of tickets that were actually used in the block. It
	// would probably be more efficient to use an array.
	usedTickets := make(map[chainhash.Hash]struct{})
	spendingHashes := make(map[chainhash.Hash]chainhash.Hash)
	revocations := make(map[chainhash.Hash]struct{})

	for _, staketx := range block.STransactions() {
		if is, _ := IsSSGen(staketx); is {
			msgTx := staketx.MsgTx()
			sstxIn := msgTx.TxIn[1] // sstx input
			sstxHash := sstxIn.PreviousOutPoint.Hash

			usedTickets[sstxHash] = struct{}{}
			spendingHashes[sstxHash] = *staketx.Sha()
		}

		if is, _ := IsSSRtx(staketx); is {
			msgTx := staketx.MsgTx()
			sstxIn := msgTx.TxIn[0] // sstx input
			sstxHash := sstxIn.PreviousOutPoint.Hash

			revocations[sstxHash] = struct{}{}
		}
	}

	// Spend or miss all the necessary tickets and do some sanity checks.
	parentBlock, err := tmdb.database.FetchBlockBySha(
		&block.MsgBlock().Header.PrevBlock)
	if err != nil {
		return nil, nil, nil, err
	}
	spentAndMissedTickets, err := tmdb.spendTickets(parentBlock,
		usedTickets,
		spendingHashes)
	if err != nil {
		return nil, nil, nil, err
	}

	// Expire all old tickets, and stick them into the spent and missed ticket
	// map too.
	expiredTickets, err := tmdb.expireTickets(height)
	if err != nil {
		return nil, nil, nil, err
	}
	if len(expiredTickets) > 0 && len(spentAndMissedTickets) == 0 {
		return nil, nil, nil, fmt.Errorf("tried to expire tickets before " +
			"stake validation height! TicketExpiry may be too small")
	}
	if len(expiredTickets) > 0 {
		for hash, ticket := range expiredTickets {
			spentAndMissedTickets[hash] = ticket
		}
	}

	revokedTickets, err := tmdb.revokeTickets(revocations)
	if err != nil {
		return nil, nil, nil, err
	}

	newTickets, err := tmdb.pushMatureTicketsAtHeight(block.Height())
	if err != nil {
		return nil, nil, nil, err
	}

	log.Debugf("Connected block %v (height %v) to the ticket database",
		block.Sha(), block.Height())

	return cloneSStxMemMap(spentAndMissedTickets), cloneSStxMemMap(newTickets),
		cloneSStxMemMap(revokedTickets), nil
}
Exemplo n.º 28
0
func testFetchTxByShaListCommon(tc *testContext, includeSpent bool) bool {
	var blockPrev *dcrutil.Block = nil
	if tc.block.Height() != 0 {
		var errBlockPrev error
		blockPrev, errBlockPrev = tc.db.FetchBlockBySha(&tc.block.MsgBlock().Header.PrevBlock)
		if errBlockPrev != nil {
			blockSha := tc.block.Sha()
			tc.t.Errorf("Failed to fetch parent block of block %v", blockSha)
		}
	}

	unspentFromTxTreeStake := unspendStakeTxTree(tc.block)

	votebits := tc.block.MsgBlock().Header.VoteBits
	if dcrutil.IsFlagSet16(votebits, dcrutil.BlockValid) && blockPrev != nil {
		fetchFunc := tc.db.FetchUnSpentTxByShaList
		funcName := "FetchUnSpentTxByShaList"
		if includeSpent {
			fetchFunc = tc.db.FetchTxByShaList
			funcName = "FetchTxByShaList"
		}

		transactions := blockPrev.Transactions()
		txHashes := make([]*chainhash.Hash, len(transactions))
		for i, tx := range transactions {
			txHashes[i] = tx.Sha()
		}

		txReplyList := fetchFunc(txHashes)
		if len(txReplyList) != len(txHashes) {
			tc.t.Errorf("%s (%s): block #%d (%s) tx reply list does not "+
				" match expected length - got: %v, want: %v", funcName,
				tc.dbType, tc.blockHeight, tc.blockHash,
				len(txReplyList), len(txHashes))
			return false
		}
		for i, tx := range transactions {
			txHash := tx.Sha()
			txD := txReplyList[i]

			// The transaction hash in the reply must be the expected value.
			if !txD.Sha.IsEqual(txHash) {
				tc.t.Errorf("%s (%s): block #%d (%s) tx #%d (%s) "+
					"hash does not match expected value - got %v",
					funcName, tc.dbType, tc.blockHeight,
					tc.blockHash, i, txHash, txD.Sha)
				return false
			}

			// The reply must not indicate any errors.
			if txD.Err != nil {
				tc.t.Errorf("%s (%s): block #%d (%s) tx #%d (%s) "+
					"returned unexpected error - got %v, want nil",
					funcName, tc.dbType, tc.blockHeight,
					tc.blockHash, i, txHash, txD.Err)
				return false
			}

			// The transaction in the reply fetched from the database must
			// be the same MsgTx that was stored.
			if !reflect.DeepEqual(tx.MsgTx(), txD.Tx) {
				tc.t.Errorf("%s (%s): block #%d (%s) tx #%d (%s) does "+
					"not match stored tx\ngot: %v\nwant: %v",
					funcName, tc.dbType, tc.blockHeight,
					tc.blockHash, i, txHash, spew.Sdump(txD.Tx),
					spew.Sdump(tx.MsgTx()))
				return false
			}

			// The block hash in the reply from the database must be the
			// expected value.
			if txD.BlkSha == nil {
				tc.t.Errorf("%s (%s): block #%d (%s) tx #%d (%s) "+
					"returned nil block hash", funcName, tc.dbType,
					tc.blockHeight, tc.blockHash, i, txHash)
				return false
			}
			if !txD.BlkSha.IsEqual(&tc.block.MsgBlock().Header.PrevBlock) {
				tc.t.Errorf("%s (%s): block #%d (%s) tx #%d (%s)"+
					"returned unexpected block hash - got %v",
					funcName, tc.dbType, tc.blockHeight,
					tc.blockHash, i, txHash, txD.BlkSha)
				return false
			}

			// The block height in the reply from the database must be the
			// expected value.
			if txD.Height != tc.blockHeight-1 {
				tc.t.Errorf("%s (%s): block #%d (%s) tx #%d (%s) "+
					"returned unexpected block height - got %v",
					funcName, tc.dbType, tc.blockHeight,
					tc.blockHash, i, txHash, txD.Height)
				return false
			}
			// The spend data in the reply from the database must not
			// indicate any of the transactions that were just inserted are
			// spent.
			if txD.TxSpent == nil {
				tc.t.Errorf("%s (%s): block #%d (%s) tx #%d (%s) "+
					"returned nil spend data", funcName, tc.dbType,
					tc.blockHeight, tc.blockHash, i, txHash)
				return false
			}
			spentBuf := expectedSpentBuf(tc, i)
			if !reflect.DeepEqual(txD.TxSpent, spentBuf) {
				stakeInChecksDontPass := false
				for txoIdx, _ := range spentBuf {
					if txD.TxSpent[txoIdx] != spentBuf[txoIdx] {
						op := wire.OutPoint{
							*txHash,
							uint32(txoIdx),
							dcrutil.TxTreeRegular,
						}

						if _, unspent := unspentFromTxTreeStake[op]; !unspent {
							stakeInChecksDontPass = true
						}
					}
				}

				if stakeInChecksDontPass {
					tc.t.Errorf("%s (%s): block #%d (%s) tx #%d (%s) "+
						"returned unexpected spend data - got %v, "+
						"want %v", funcName, tc.dbType, tc.blockHeight,
						tc.blockHash, i, txHash, txD.TxSpent, spentBuf)
					return false
				}
			}
		}
	}
	return true
}
Exemplo n.º 29
0
// expectedSpentBuf returns the expected transaction spend information depending
// on the block height and and transaction number.  NOTE: These figures are
// only valid for the specific set of test data provided at the time these tests
// were written.  In particular, this means the first 256 blocks of the mainnet
// block chain.
//
// The first run through while the blocks are still being inserted, the tests
// are running against the latest block and therefore none of the outputs can
// be spent yet.  However, on subsequent runs, all blocks have been inserted and
// therefore some of the transaction outputs are spent.
func expectedSpentBuf(tc *testContext, txNum int) []bool {
	var blah = []bool{false}
	var blockPrev *dcrutil.Block = nil
	if tc.block.Height() != 0 {
		var errBlockPrev error
		blockPrev, errBlockPrev = tc.db.FetchBlockBySha(&tc.block.MsgBlock().Header.PrevBlock)
		if errBlockPrev != nil {
			blockSha := tc.block.Sha()
			tc.t.Errorf("Failed to fetch parent block of block %v", blockSha)
			return blah
		}
	}
	transactions := blockPrev.Transactions()
	numTxOut := len(transactions[txNum].MsgTx().TxOut)
	spentBuf := make([]bool, numTxOut)
	if tc.useSpends {
		if tc.blockHeight >= 2 && tc.blockHeight <= 43 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 45 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 46 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 48 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 49 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 54 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 55 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 57 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 59 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 63 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 67 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 68 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 69 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 70 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 73 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 74 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 76 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 77 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight >= 105 && tc.blockHeight <= 120 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 122 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 125 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 127 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 131 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 132 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 134 && txNum == 0 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
		}
		if tc.blockHeight == 44 && txNum == 1 {
			spentBuf[0] = true
			spentBuf[1] = true
			spentBuf[2] = true
			spentBuf[3] = true
			spentBuf[4] = false
			spentBuf[5] = true
		}
		if tc.blockHeight == 60 && txNum == 1 {
			spentBuf[0] = false
			spentBuf[1] = true
			spentBuf[2] = false
			spentBuf[3] = false
			spentBuf[4] = false
			spentBuf[5] = false
		}
		if tc.blockHeight == 75 && txNum == 1 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = false
			spentBuf[3] = false
			spentBuf[4] = false
			spentBuf[5] = true
		}
		if tc.blockHeight == 78 && txNum == 2 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = false
			spentBuf[3] = false
			spentBuf[4] = false
			spentBuf[5] = true
		}
		if tc.blockHeight == 79 && txNum == 1 {
			spentBuf[0] = false
			spentBuf[1] = true
			spentBuf[2] = false
			spentBuf[3] = false
			spentBuf[4] = false
			spentBuf[5] = false
		}
		if tc.blockHeight == 89 && txNum == 1 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
			spentBuf[3] = false
			spentBuf[4] = false
			spentBuf[5] = false
		}
		if tc.blockHeight == 90 && txNum == 1 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = false
			spentBuf[3] = false
			spentBuf[4] = false
			spentBuf[5] = true
		}
		if tc.blockHeight == 93 && txNum == 1 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = false
			spentBuf[3] = false
			spentBuf[4] = false
			spentBuf[5] = true
		}
		if tc.blockHeight == 95 && txNum == 1 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
			spentBuf[3] = false
			spentBuf[4] = false
			spentBuf[5] = false
		}
		if tc.blockHeight == 97 && txNum == 3 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = false
			spentBuf[3] = true
			spentBuf[4] = false
			spentBuf[5] = false
		}
		if tc.blockHeight == 99 && txNum == 1 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = false
			spentBuf[3] = true
			spentBuf[4] = false
			spentBuf[5] = false
		}
		if tc.blockHeight == 101 && txNum == 1 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = false
			spentBuf[3] = false
			spentBuf[4] = false
			spentBuf[5] = true
		}
		if tc.blockHeight == 103 && txNum == 1 {
			spentBuf[0] = true
			spentBuf[1] = false
			spentBuf[2] = false
			spentBuf[3] = false
			spentBuf[4] = false
			spentBuf[5] = false
		}
		if tc.blockHeight == 106 && txNum == 1 {
			spentBuf[0] = true
			spentBuf[1] = true
			spentBuf[2] = true
			spentBuf[3] = true
			spentBuf[4] = true
			spentBuf[5] = false
		}
		if tc.blockHeight == 111 && txNum == 1 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = false
			spentBuf[3] = false
			spentBuf[4] = false
			spentBuf[5] = true
		}
		if tc.blockHeight == 113 && txNum == 1 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = false
			spentBuf[3] = false
			spentBuf[4] = true
			spentBuf[5] = false
		}
		if tc.blockHeight == 113 && txNum == 2 {
			spentBuf[0] = true
			spentBuf[1] = false
			spentBuf[2] = false
			spentBuf[3] = false
			spentBuf[4] = false
			spentBuf[5] = true
		}
		if tc.blockHeight == 117 && txNum == 1 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
			spentBuf[3] = false
			spentBuf[4] = false
			spentBuf[5] = false
		}
		if tc.blockHeight == 122 && txNum == 1 {
			spentBuf[0] = true
			spentBuf[1] = true
			spentBuf[2] = true
			spentBuf[3] = false
			spentBuf[4] = true
			spentBuf[5] = true
		}
		if tc.blockHeight == 131 && txNum == 1 {
			spentBuf[0] = true
			spentBuf[1] = false
			spentBuf[2] = true
			spentBuf[3] = true
			spentBuf[4] = true
			spentBuf[5] = true
		}
		if tc.blockHeight == 135 && txNum == 1 {
			spentBuf[0] = true
			spentBuf[1] = true
			spentBuf[2] = true
			spentBuf[3] = true
			spentBuf[4] = true
			spentBuf[5] = false
		}
		if tc.blockHeight == 141 && txNum == 1 {
			spentBuf[0] = true
			spentBuf[1] = true
			spentBuf[2] = true
			spentBuf[3] = true
			spentBuf[4] = false
			spentBuf[5] = true
		}
		if tc.blockHeight == 142 && txNum == 1 {
			spentBuf[0] = true
			spentBuf[1] = true
			spentBuf[2] = false
			spentBuf[3] = true
			spentBuf[4] = true
			spentBuf[5] = true
		}
		if tc.blockHeight == 145 && txNum == 1 {
			spentBuf[0] = true
			spentBuf[1] = false
			spentBuf[2] = true
			spentBuf[3] = true
			spentBuf[4] = true
			spentBuf[5] = true
		}
		if tc.blockHeight == 146 && txNum == 1 {
			spentBuf[0] = true
			spentBuf[1] = false
			spentBuf[2] = false
			spentBuf[3] = true
			spentBuf[4] = false
			spentBuf[5] = true
		}
		if tc.blockHeight == 146 && txNum == 2 {
			spentBuf[0] = true
			spentBuf[1] = true
			spentBuf[2] = true
			spentBuf[3] = true
			spentBuf[4] = false
			spentBuf[5] = true
		}
		if tc.blockHeight == 147 && txNum == 1 {
			spentBuf[0] = false
			spentBuf[1] = false
			spentBuf[2] = true
			spentBuf[3] = false
			spentBuf[4] = true
			spentBuf[5] = false
		}
	}

	return spentBuf
}
Exemplo n.º 30
0
// testFetchTxBySha ensures FetchTxBySha conforms to the interface contract.
func testFetchTxBySha(tc *testContext) bool {
	var blockPrev *dcrutil.Block = nil
	if tc.block.Height() != 0 {
		var errBlockPrev error
		blockPrev, errBlockPrev = tc.db.FetchBlockBySha(&tc.block.MsgBlock().Header.PrevBlock)
		if errBlockPrev != nil {
			blockSha := tc.block.Sha()
			tc.t.Errorf("Failed to fetch parent block of block %v", blockSha)
		}
	}

	votebits := tc.block.MsgBlock().Header.VoteBits
	if dcrutil.IsFlagSet16(votebits, dcrutil.BlockValid) && blockPrev != nil {
		for i, tx := range blockPrev.Transactions() {
			txHash := tx.Sha()
			txReplyList, err := tc.db.FetchTxBySha(txHash)
			if err != nil {
				tc.t.Errorf("FetchTxBySha (%s): block #%d (%s) "+
					"tx #%d (%s) err: %v", tc.dbType, tc.blockHeight,
					tc.blockHash, i, txHash, err)
				return false
			}
			if len(txReplyList) == 0 {
				tc.t.Errorf("FetchTxBySha (%s): block #%d (%s) "+
					"tx #%d (%s) did not return reply data",
					tc.dbType, tc.blockHeight, tc.blockHash, i,
					txHash)
				return false
			}
			txFromDb := txReplyList[len(txReplyList)-1].Tx
			if !reflect.DeepEqual(tx.MsgTx(), txFromDb) {
				tc.t.Errorf("FetchTxBySha (%s): block #%d (%s) "+
					"tx #%d (%s, %s) does not match stored tx\n"+
					"got: %v\nwant: %v", tc.dbType, tc.blockHeight,
					tc.blockHash, i, txHash, txFromDb.TxSha(), spew.Sdump(txFromDb),
					spew.Sdump(tx.MsgTx()))
				return false
			}
		}
	}
	for i, tx := range tc.block.MsgBlock().STransactions {
		txHash := tx.TxSha()
		txReplyList, err := tc.db.FetchTxBySha(&txHash)
		if err != nil {
			tc.t.Errorf("FetchTxBySha (%s): block #%d (%s) "+
				"sstx #%d (%s) err: %v", tc.dbType, tc.blockHeight,
				tc.blockHash, i, txHash, err)
			return false
		}
		if len(txReplyList) == 0 {
			tc.t.Errorf("FetchTxBySha (%s): block #%d (%s) "+
				"sstx #%d (%s) did not return reply data",
				tc.dbType, tc.blockHeight, tc.blockHash, i,
				txHash)
			return false
		}
		txFromDb := txReplyList[len(txReplyList)-1].Tx
		if !reflect.DeepEqual(tx, txFromDb) {
			tc.t.Errorf("FetchTxBySha (%s): block #%d (%s) "+
				"sstx #%d (%s) does not match stored sstx\n"+
				"got: %v\nwant: %v", tc.dbType, tc.blockHeight,
				tc.blockHash, i, txHash, spew.Sdump(txFromDb),
				spew.Sdump(tx))
			return false
		}
	}

	return true
}