Example #1
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
}
Example #2
0
// DebugBlockString dumps a verbose message containing information about
// the transactions of a block.
func DebugBlockString(block *dcrutil.Block) string {
	if block == nil {
		return "block pointer nil"
	}

	var buffer bytes.Buffer

	hash := block.Sha()

	str := fmt.Sprintf("Block Header: %v Height: %v \n",
		hash, block.Height())
	buffer.WriteString(str)

	str = fmt.Sprintf("Block contains %v regular transactions "+
		"and %v stake transactions \n",
		len(block.Transactions()),
		len(block.STransactions()))
	buffer.WriteString(str)

	str = fmt.Sprintf("List of regular transactions \n")
	buffer.WriteString(str)

	for i, tx := range block.Transactions() {
		str = fmt.Sprintf("Index: %v, Hash: %v \n", i, tx.Sha())
		buffer.WriteString(str)
	}

	if len(block.STransactions()) == 0 {
		return buffer.String()
	}

	str = fmt.Sprintf("List of stake transactions \n")
	buffer.WriteString(str)

	for i, stx := range block.STransactions() {
		txTypeStr := ""
		txType := stake.DetermineTxType(stx)
		switch txType {
		case stake.TxTypeSStx:
			txTypeStr = "SStx"
		case stake.TxTypeSSGen:
			txTypeStr = "SSGen"
		case stake.TxTypeSSRtx:
			txTypeStr = "SSRtx"
		default:
			txTypeStr = "Error"
		}

		str = fmt.Sprintf("Index: %v, Type: %v, Hash: %v \n",
			i, txTypeStr, stx.Sha())
		buffer.WriteString(str)
	}

	return buffer.String()
}
Example #3
0
// connectTransactions updates the view by adding all new utxos created by all
// of the transactions in the passed block, marking all utxos the transactions
// spend as spent, and setting the best hash for the view to the passed block.
// In addition, when the 'stxos' argument is not nil, it will be updated to
// append an entry for each spent txout.
func (b *BlockChain) connectTransactions(view *UtxoViewpoint, block *dcrutil.Block,
	parent *dcrutil.Block, stxos *[]spentTxOut) error {
	regularTxTreeValid := dcrutil.IsFlagSet16(block.MsgBlock().Header.VoteBits,
		dcrutil.BlockValid)
	thisNodeStakeViewpoint := ViewpointPrevInvalidStake
	if regularTxTreeValid {
		thisNodeStakeViewpoint = ViewpointPrevValidStake
	}

	if parent != nil && block.Height() != 0 {
		view.SetStakeViewpoint(ViewpointPrevValidInitial)
		err := view.fetchInputUtxos(b.db, block, parent)
		if err != nil {
			return err
		}
		mBlock := block.MsgBlock()
		votebits := mBlock.Header.VoteBits
		regularTxTreeValid := dcrutil.IsFlagSet16(votebits, dcrutil.BlockValid)
		if regularTxTreeValid {
			for i, tx := range parent.Transactions() {
				err := view.connectTransaction(tx, parent.Height(), uint32(i),
					stxos)
				if err != nil {
					return err
				}
			}
		}
	}

	for i, stx := range block.STransactions() {
		view.SetStakeViewpoint(thisNodeStakeViewpoint)
		err := view.fetchInputUtxos(b.db, block, parent)
		if err != nil {
			return err
		}
		err = view.connectTransaction(stx, block.Height(), uint32(i), stxos)
		if err != nil {
			return err
		}
	}

	// Update the best hash for view to include this block since all of its
	// transactions have been connected.
	view.SetBestHash(block.Sha())
	return nil
}
Example #4
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
}
Example #5
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
}
Example #6
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
}
Example #7
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
}
Example #8
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
}
Example #9
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
}
Example #10
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
}
Example #11
0
// lookupTransaction is a special transaction lookup function that searches
// the database, the block, and its parent for a transaction. This is needed
// because indexBlockAddrs is called AFTER a block is added/removed in the
// blockchain in blockManager, necessitating that the blocks internally be
// searched for inputs for any given transaction too. Additionally, it's faster
// to get the tx from the blocks here since they're already
func (a *addrIndexer) lookupTransaction(txHash chainhash.Hash, blk *dcrutil.Block,
	parent *dcrutil.Block) (*wire.MsgTx, error) {
	// Search the previous block and parent first.
	txTreeRegularValid := dcrutil.IsFlagSet16(blk.MsgBlock().Header.VoteBits,
		dcrutil.BlockValid)

	// Search the regular tx tree of this and the last block if the
	// tx tree regular was validated.
	if txTreeRegularValid {
		for _, stx := range parent.STransactions() {
			if stx.Sha().IsEqual(&txHash) {
				return stx.MsgTx(), nil
			}
		}
		for _, tx := range parent.Transactions() {
			if tx.Sha().IsEqual(&txHash) {
				return tx.MsgTx(), nil
			}
		}
		for _, tx := range blk.Transactions() {
			if tx.Sha().IsEqual(&txHash) {
				return tx.MsgTx(), nil
			}
		}
	} else {
		// Just search this block's regular tx tree and the previous
		// block's stake tx tree.
		for _, stx := range parent.STransactions() {
			if stx.Sha().IsEqual(&txHash) {
				return stx.MsgTx(), nil
			}
		}
		for _, tx := range blk.Transactions() {
			if tx.Sha().IsEqual(&txHash) {
				return tx.MsgTx(), nil
			}
		}
	}

	// Lookup and fetch the referenced output's tx in the database.
	txList, err := a.server.db.FetchTxBySha(&txHash)
	if err != nil {
		adxrLog.Errorf("Error fetching tx %v: %v",
			txHash, err)
		return nil, err
	}

	if len(txList) == 0 {
		return nil, fmt.Errorf("transaction %v not found",
			txHash)
	}

	return txList[len(txList)-1].Tx, nil
}
Example #12
0
// IsCheckpointCandidate returns whether or not the passed block is a good
// checkpoint candidate.
//
// The factors used to determine a good checkpoint are:
//  - The block must be in the main chain
//  - The block must be at least 'CheckpointConfirmations' blocks prior to the
//    current end of the main chain
//  - The timestamps for the blocks before and after the checkpoint must have
//    timestamps which are also before and after the checkpoint, respectively
//    (due to the median time allowance this is not always the case)
//  - The block must not contain any strange transaction such as those with
//    nonstandard scripts
//
// The intent is that candidates are reviewed by a developer to make the final
// decision and then manually added to the list of checkpoints for a network.
//
// This function is safe for concurrent access.
func (b *BlockChain) IsCheckpointCandidate(block *dcrutil.Block) (bool, error) {
	b.chainLock.RLock()
	defer b.chainLock.RUnlock()

	// Checkpoints must be enabled.
	if b.noCheckpoints {
		return false, fmt.Errorf("checkpoints are disabled")
	}

	var isCandidate bool
	err := b.db.View(func(dbTx database.Tx) error {
		// A checkpoint must be in the main chain.
		blockHeight, err := dbFetchHeightByHash(dbTx, block.Sha())
		if err != nil {
			// Only return an error if it's not due to the block not
			// being in the main chain.
			if !isNotInMainChainErr(err) {
				return err
			}
			return nil
		}

		// Ensure the height of the passed block and the entry for the
		// block in the main chain match.  This should always be the
		// case unless the caller provided an invalid block.
		if blockHeight != block.Height() {
			return fmt.Errorf("passed block height of %d does not "+
				"match the main chain height of %d",
				block.Height(), blockHeight)
		}

		// A checkpoint must be at least CheckpointConfirmations blocks
		// before the end of the main chain.
		mainChainHeight := b.bestNode.height
		if blockHeight > (mainChainHeight - CheckpointConfirmations) {
			return nil
		}

		// Get the previous block header.
		prevHash := &block.MsgBlock().Header.PrevBlock
		prevHeader, err := dbFetchHeaderByHash(dbTx, prevHash)
		if err != nil {
			return err
		}

		// Get the next block header.
		nextHeader, err := dbFetchHeaderByHeight(dbTx, blockHeight+1)
		if err != nil {
			return err
		}

		// A checkpoint must have timestamps for the block and the
		// blocks on either side of it in order (due to the median time
		// allowance this is not always the case).
		prevTime := prevHeader.Timestamp
		curTime := block.MsgBlock().Header.Timestamp
		nextTime := nextHeader.Timestamp
		if prevTime.After(curTime) || nextTime.Before(curTime) {
			return nil
		}

		// A checkpoint must have transactions that only contain
		// standard scripts.
		for _, tx := range block.Transactions() {
			if isNonstandardTransaction(tx) {
				return nil
			}
		}

		// All of the checks passed, so the block is a candidate.
		isCandidate = true
		return nil
	})
	return isCandidate, err
}
Example #13
0
// DropAfterBlockBySha will remove any blocks from the database after
// the given block.
func (db *LevelDb) DropAfterBlockBySha(sha *chainhash.Hash) (rerr error) {
	db.dbLock.Lock()
	defer db.dbLock.Unlock()
	defer func() {
		if rerr == nil {
			rerr = db.processBatches()
		} else {
			db.lBatch().Reset()
		}
	}()

	startheight := db.nextBlock - 1

	keepidx, err := db.getBlkLoc(sha)
	if err != nil {
		// should the error here be normalized ?
		log.Tracef("block loc failed %v ", sha)
		return err
	}

	for height := startheight; height > keepidx; height = height - 1 {
		var blk *dcrutil.Block
		blksha, buf, err := db.getBlkByHeight(height)
		if err != nil {
			return err
		}

		blk, err = dcrutil.NewBlockFromBytes(buf)
		if err != nil {
			return err
		}

		// Obtain previous block sha and buffer
		var blkprev *dcrutil.Block
		_, bufprev, errprev := db.getBlkByHeight(height - 1) // discard blkshaprev
		if errprev != nil {
			return errprev
		}

		// Do the same thing for the parent block
		blkprev, errprev = dcrutil.NewBlockFromBytes(bufprev)
		if errprev != nil {
			return errprev
		}

		// Unspend the stake tx in the current block
		for _, tx := range blk.MsgBlock().STransactions {
			err = db.unSpend(tx)
			if err != nil {
				return err
			}
		}
		// rather than iterate the list of tx backward, do it twice.
		for _, tx := range blk.STransactions() {
			var txUo txUpdateObj
			txUo.delete = true
			db.txUpdateMap[*tx.Sha()] = &txUo
		}

		// Check to see if the regular txs of the parent were even included; if
		// they are, unspend all of these regular tx too
		votebits := blk.MsgBlock().Header.VoteBits
		if dcrutil.IsFlagSet16(votebits, dcrutil.BlockValid) && height != 0 {
			// Unspend the regular tx in the current block
			for _, tx := range blkprev.MsgBlock().Transactions {
				err = db.unSpend(tx)
				if err != nil {
					return err
				}
			}
			// rather than iterate the list of tx backward, do it twice.
			for _, tx := range blkprev.Transactions() {
				var txUo txUpdateObj
				txUo.delete = true
				db.txUpdateMap[*tx.Sha()] = &txUo
			}
		}

		db.lBatch().Delete(shaBlkToKey(blksha))
		db.lBatch().Delete(int64ToKey(height))
	}

	// update the last block cache
	db.lastBlkShaCached = true
	db.lastBlkSha = *sha
	db.lastBlkIdx = keepidx
	db.nextBlock = keepidx + 1

	return nil
}
Example #14
0
// makeUtxoView creates a mock unspent transaction output view by using the
// transaction index in order to look up all inputs referenced by the
// transactions in the block.  This is sometimes needed when catching indexes up
// because many of the txouts could actually already be spent however the
// associated scripts are still required to index them.
func makeUtxoView(dbTx database.Tx, block, parent *dcrutil.Block) (*blockchain.UtxoViewpoint, error) {
	view := blockchain.NewUtxoViewpoint()
	regularTxTreeValid := dcrutil.IsFlagSet16(block.MsgBlock().Header.VoteBits,
		dcrutil.BlockValid)
	if regularTxTreeValid {
		for txIdx, tx := range parent.Transactions() {
			// Coinbases do not reference any inputs.  Since the block is
			// required to have already gone through full validation, it has
			// already been proven on the first transaction in the block is
			// a coinbase.
			if txIdx == 0 {
				continue
			}

			// Use the transaction index to load all of the referenced
			// inputs and add their outputs to the view.
			for _, txIn := range tx.MsgTx().TxIn {
				// Skip already fetched outputs.
				originOut := &txIn.PreviousOutPoint
				if view.LookupEntry(&originOut.Hash) != nil {
					continue
				}

				originTx, err := dbFetchTx(dbTx, originOut.Hash)
				if err != nil {
					return nil, err
				}

				view.AddTxOuts(dcrutil.NewTx(originTx),
					int64(wire.NullBlockHeight), wire.NullBlockIndex)
			}
		}
	}

	for _, tx := range block.STransactions() {
		msgTx := tx.MsgTx()
		isSSGen, _ := stake.IsSSGen(msgTx)

		// Use the transaction index to load all of the referenced
		// inputs and add their outputs to the view.
		for i, txIn := range msgTx.TxIn {
			// Skip stakebases.
			if isSSGen && i == 0 {
				continue
			}

			originOut := &txIn.PreviousOutPoint
			if view.LookupEntry(&originOut.Hash) != nil {
				continue
			}

			originTx, err := dbFetchTx(dbTx, originOut.Hash)
			if err != nil {
				return nil, err
			}

			view.AddTxOuts(dcrutil.NewTx(originTx), int64(wire.NullBlockHeight),
				wire.NullBlockIndex)
		}
	}

	return view, nil
}
Example #15
0
// findWhereDoubleSpent determines where a tx was previously doublespent.
// VERY INTENSIVE BLOCKCHAIN SCANNING, USE TO DEBUG SIMULATED BLOCKCHAINS
// ONLY.
func (b *BlockChain) findWhereDoubleSpent(block *dcrutil.Block) error {
	height := int64(1)
	heightEnd := block.Height()

	hashes, err := b.db.FetchHeightRange(height, heightEnd)
	if err != nil {
		return err
	}

	var allTxs []*dcrutil.Tx
	txs := block.Transactions()[1:]
	stxs := block.STransactions()
	allTxs = append(txs, stxs...)

	for _, hash := range hashes {
		curBlock, err := b.getBlockFromHash(&hash)
		if err != nil {
			return err
		}
		log.Errorf("Cur block %v", curBlock.Height())

		for _, localTx := range allTxs {
			for _, localTxIn := range localTx.MsgTx().TxIn {
				for _, tx := range curBlock.Transactions()[1:] {
					for _, txIn := range tx.MsgTx().TxIn {
						if txIn.PreviousOutPoint == localTxIn.PreviousOutPoint {
							log.Errorf("Double spend of {hash: %v, idx: %v,"+
								" tree: %b}, previously found in tx %v "+
								"of block %v txtree regular",
								txIn.PreviousOutPoint.Hash,
								txIn.PreviousOutPoint.Index,
								txIn.PreviousOutPoint.Tree,
								tx.Sha(),
								hash)
						}
					}
				}

				for _, tx := range curBlock.STransactions() {
					for _, txIn := range tx.MsgTx().TxIn {
						if txIn.PreviousOutPoint == localTxIn.PreviousOutPoint {
							log.Errorf("Double spend of {hash: %v, idx: %v,"+
								" tree: %b}, previously found in tx %v "+
								"of block %v txtree stake\n",
								txIn.PreviousOutPoint.Hash,
								txIn.PreviousOutPoint.Index,
								txIn.PreviousOutPoint.Tree,
								tx.Sha(),
								hash)
						}
					}
				}
			}
		}
	}

	for _, localTx := range stxs {
		for _, localTxIn := range localTx.MsgTx().TxIn {
			for _, tx := range txs {
				for _, txIn := range tx.MsgTx().TxIn {
					if txIn.PreviousOutPoint == localTxIn.PreviousOutPoint {
						log.Errorf("Double spend of {hash: %v, idx: %v,"+
							" tree: %b}, previously found in tx %v "+
							"of cur block stake txtree\n",
							txIn.PreviousOutPoint.Hash,
							txIn.PreviousOutPoint.Index,
							txIn.PreviousOutPoint.Tree,
							tx.Sha())
					}
				}
			}
		}
	}

	return nil
}
Example #16
0
// dbAddTxIndexEntries uses an existing database transaction to add a
// transaction index entry for every transaction in the passed block.
func dbAddTxIndexEntries(dbTx database.Tx, block, parent *dcrutil.Block, blockID uint32) error {
	// The offset and length of the transactions within the serialized
	// block, for the regular transactions of the parent (if added)
	// and the stake transactions of the current block.
	regularTxTreeValid := dcrutil.IsFlagSet16(block.MsgBlock().Header.VoteBits,
		dcrutil.BlockValid)
	var parentRegularTxs []*dcrutil.Tx
	var parentTxLocs []wire.TxLoc
	var parentBlockID uint32
	if regularTxTreeValid && block.Height() > 1 {
		var err error
		parentRegularTxs = parent.Transactions()

		parentTxLocs, _, err = parent.TxLoc()
		if err != nil {
			return err
		}

		parentSha := parent.Sha()
		parentBlockID, err = dbFetchBlockIDByHash(dbTx, *parentSha)
		if err != nil {
			return err
		}
	}
	_, blockStxLocs, err := block.TxLoc()
	if err != nil {
		return err
	}

	allTxs := append(parentRegularTxs, block.STransactions()...)
	allTxsLocs := append(parentTxLocs, blockStxLocs...)
	stakeTxStartIdx := len(parentRegularTxs)

	// As an optimization, allocate a single slice big enough to hold all
	// of the serialized transaction index entries for the block and
	// serialize them directly into the slice.  Then, pass the appropriate
	// subslice to the database to be written.  This approach significantly
	// cuts down on the number of required allocations.
	offset := 0
	serializedValues := make([]byte, len(allTxs)*txEntrySize)
	blockIDToUse := parentBlockID
	for i, tx := range allTxs {
		// Switch to using the newest block ID for the stake transactions,
		// since these are not from the parent.
		if i == stakeTxStartIdx {
			blockIDToUse = blockID
		}

		putTxIndexEntry(serializedValues[offset:], blockIDToUse, allTxsLocs[i])
		endOffset := offset + txEntrySize
		txSha := tx.Sha()
		err := dbPutTxIndexEntry(dbTx, *txSha,
			serializedValues[offset:endOffset:endOffset])
		if err != nil {
			return err
		}
		offset += txEntrySize
	}

	return nil
}
Example #17
0
// disconnectTransactions updates the view by removing all of the transactions
// created by the passed block, restoring all utxos the transactions spent by
// using the provided spent txo information, and setting the best hash for the
// view to the block before the passed block.
//
// This function will ONLY work correctly for a single transaction tree at a
// time because of index tracking.
func (b *BlockChain) disconnectTransactions(view *UtxoViewpoint,
	block *dcrutil.Block, parent *dcrutil.Block, stxos []spentTxOut) error {
	// Sanity check the correct number of stxos are provided.
	if len(stxos) != countSpentOutputs(block, parent) {
		return AssertError(fmt.Sprintf("disconnectTransactions "+
			"called with bad spent transaction out information "+
			"(len stxos %v, count is %v)", len(stxos),
			countSpentOutputs(block, parent)))
	}

	// Loop backwards through all transactions so everything is unspent in
	// reverse order.  This is necessary since transactions later in a block
	// can spend from previous ones.
	regularTxTreeValid := dcrutil.IsFlagSet16(block.MsgBlock().Header.VoteBits,
		dcrutil.BlockValid)
	thisNodeStakeViewpoint := ViewpointPrevInvalidStake
	if regularTxTreeValid {
		thisNodeStakeViewpoint = ViewpointPrevValidStake
	}
	view.SetStakeViewpoint(thisNodeStakeViewpoint)
	err := view.fetchInputUtxos(b.db, block, parent)
	if err != nil {
		return err
	}
	stxoIdx := len(stxos) - 1
	transactions := block.STransactions()
	for txIdx := len(transactions) - 1; txIdx > -1; txIdx-- {
		tx := transactions[txIdx]
		msgTx := tx.MsgTx()
		tt := stake.DetermineTxType(msgTx)

		// Clear this transaction from the view if it already exists or
		// create a new empty entry for when it does not.  This is done
		// because the code relies on its existence in the view in order
		// to signal modifications have happened.
		entry := view.entries[*tx.Sha()]
		if entry == nil {
			entry = newUtxoEntry(msgTx.Version, uint32(block.Height()),
				uint32(txIdx), IsCoinBaseTx(msgTx), msgTx.Expiry != 0, tt)
			if tt == stake.TxTypeSStx {
				stakeExtra := make([]byte, serializeSizeForMinimalOutputs(tx))
				putTxToMinimalOutputs(stakeExtra, tx)
				entry.stakeExtra = stakeExtra
			}
			view.entries[*tx.Sha()] = entry
		}
		entry.modified = true
		entry.sparseOutputs = make(map[uint32]*utxoOutput)

		// Loop backwards through all of the transaction inputs (except
		// for the coinbase which has no inputs) and unspend the
		// referenced txos.  This is necessary to match the order of the
		// spent txout entries.
		for txInIdx := len(tx.MsgTx().TxIn) - 1; txInIdx > -1; txInIdx-- {
			// Skip empty vote stakebases.
			if txInIdx == 0 && (tt == stake.TxTypeSSGen) {
				continue
			}

			// Ensure the spent txout index is decremented to stay
			// in sync with the transaction input.
			stxo := &stxos[stxoIdx]
			stxoIdx--

			// When there is not already an entry for the referenced
			// transaction in the view, it means it was fully spent,
			// so create a new utxo entry in order to resurrect it.
			txIn := tx.MsgTx().TxIn[txInIdx]
			originHash := &txIn.PreviousOutPoint.Hash
			originIndex := txIn.PreviousOutPoint.Index
			entry := view.LookupEntry(originHash)
			if entry == nil {
				if !stxo.txFullySpent {
					return AssertError(fmt.Sprintf("tried to revive utx %v from "+
						"non-fully spent stx entry", originHash))
				}

				entry = newUtxoEntry(tx.MsgTx().Version, stxo.height,
					stxo.index, stxo.isCoinBase, stxo.hasExpiry,
					stxo.txType)
				if stxo.txType == stake.TxTypeSStx {
					entry.stakeExtra = stxo.stakeExtra
				}
				view.entries[*originHash] = entry
			}

			// Mark the entry as modified since it is either new
			// or will be changed below.
			entry.modified = true

			// Restore the specific utxo using the stxo data from
			// the spend journal if it doesn't already exist in the
			// view.
			output, ok := entry.sparseOutputs[originIndex]
			if !ok {
				// Add the unspent transaction output.
				entry.sparseOutputs[originIndex] = &utxoOutput{
					compressed:    stxo.compressed,
					spent:         false,
					amount:        txIn.ValueIn,
					scriptVersion: stxo.scriptVersion,
					pkScript:      stxo.pkScript,
				}
				continue
			}

			// Mark the existing referenced transaction output as
			// unspent.
			output.spent = false
		}
	}

	// There is no regular tx from before the genesis block, so ignore the genesis
	// block for the next step.
	if parent != nil && block.Height() != 0 {
		// Only bother to unspend transactions if the parent's tx tree was
		// validated. Otherwise, these transactions were never in the blockchain's
		// history in the first place.
		if regularTxTreeValid {
			view.SetStakeViewpoint(ViewpointPrevValidInitial)
			err = view.fetchInputUtxos(b.db, block, parent)
			if err != nil {
				return err
			}

			transactions := parent.Transactions()
			for txIdx := len(transactions) - 1; txIdx > -1; txIdx-- {
				tx := transactions[txIdx]

				// Clear this transaction from the view if it already exists or
				// create a new empty entry for when it does not.  This is done
				// because the code relies on its existence in the view in order
				// to signal modifications have happened.
				isCoinbase := txIdx == 0
				entry := view.entries[*tx.Sha()]
				if entry == nil {
					entry = newUtxoEntry(tx.MsgTx().Version,
						uint32(parent.Height()), uint32(txIdx), isCoinbase,
						tx.MsgTx().Expiry != 0, stake.TxTypeRegular)
					view.entries[*tx.Sha()] = entry
				}
				entry.modified = true
				entry.sparseOutputs = make(map[uint32]*utxoOutput)

				// Loop backwards through all of the transaction inputs (except
				// for the coinbase which has no inputs) and unspend the
				// referenced txos.  This is necessary to match the order of the
				// spent txout entries.
				if isCoinbase {
					continue
				}
				for txInIdx := len(tx.MsgTx().TxIn) - 1; txInIdx > -1; txInIdx-- {
					// Ensure the spent txout index is decremented to stay
					// in sync with the transaction input.
					stxo := &stxos[stxoIdx]
					stxoIdx--

					// When there is not already an entry for the referenced
					// transaction in the view, it means it was fully spent,
					// so create a new utxo entry in order to resurrect it.
					txIn := tx.MsgTx().TxIn[txInIdx]
					originHash := &txIn.PreviousOutPoint.Hash
					originIndex := txIn.PreviousOutPoint.Index
					entry := view.entries[*originHash]
					if entry == nil {
						if !stxo.txFullySpent {
							return AssertError(fmt.Sprintf("tried to "+
								"revive utx %v from non-fully spent stx entry",
								originHash))
						}
						entry = newUtxoEntry(tx.MsgTx().Version,
							stxo.height, stxo.index, stxo.isCoinBase,
							stxo.hasExpiry, stxo.txType)
						if stxo.txType == stake.TxTypeSStx {
							entry.stakeExtra = stxo.stakeExtra
						}
						view.entries[*originHash] = entry
					}

					// Mark the entry as modified since it is either new
					// or will be changed below.
					entry.modified = true

					// Restore the specific utxo using the stxo data from
					// the spend journal if it doesn't already exist in the
					// view.
					output, ok := entry.sparseOutputs[originIndex]
					if !ok {
						// Add the unspent transaction output.
						entry.sparseOutputs[originIndex] = &utxoOutput{
							compressed:    stxo.compressed,
							spent:         false,
							amount:        txIn.ValueIn,
							scriptVersion: stxo.scriptVersion,
							pkScript:      stxo.pkScript,
						}
						continue
					}

					// Mark the existing referenced transaction output as
					// unspent.
					output.spent = false
				}
			}
		}
	}

	// Update the best hash for view to the previous block since all of the
	// transactions for the current block have been disconnected.
	view.SetBestHash(parent.Sha())
	return nil
}
Example #18
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
}
Example #19
0
// ConnectBlock is invoked by the index manager when a new block has been
// connected to the main chain.  This indexer adds a key for each address
// the transactions in the block involve.
//
// This is part of the Indexer interface.
func (idx *ExistsAddrIndex) ConnectBlock(dbTx database.Tx, block, parent *dcrutil.Block, view *blockchain.UtxoViewpoint) error {
	regularTxTreeValid := dcrutil.IsFlagSet16(block.MsgBlock().Header.VoteBits,
		dcrutil.BlockValid)
	var parentTxs []*dcrutil.Tx
	if regularTxTreeValid && block.Height() > 1 {
		parentTxs = parent.Transactions()
	}
	blockTxns := block.STransactions()
	allTxns := append(parentTxs, blockTxns...)

	usedAddrs := make(map[[addrKeySize]byte]struct{})

	for _, tx := range allTxns {
		msgTx := tx.MsgTx()
		isSStx, _ := stake.IsSStx(msgTx)
		for _, txIn := range msgTx.TxIn {
			if txscript.IsMultisigSigScript(txIn.SignatureScript) {
				rs, err :=
					txscript.MultisigRedeemScriptFromScriptSig(
						txIn.SignatureScript)
				if err != nil {
					continue
				}

				class, addrs, _, err := txscript.ExtractPkScriptAddrs(
					txscript.DefaultScriptVersion, rs, idx.chainParams)
				if err != nil {
					// Non-standard outputs are skipped.
					continue
				}
				if class != txscript.MultiSigTy {
					// This should never happen, but be paranoid.
					continue
				}

				for _, addr := range addrs {
					k, err := addrToKey(addr, idx.chainParams)
					if err != nil {
						continue
					}

					usedAddrs[k] = struct{}{}
				}
			}
		}

		for _, txOut := range tx.MsgTx().TxOut {
			class, addrs, _, err := txscript.ExtractPkScriptAddrs(
				txOut.Version, txOut.PkScript, idx.chainParams)
			if err != nil {
				// Non-standard outputs are skipped.
				continue
			}

			if isSStx && class == txscript.NullDataTy {
				addr, err := stake.AddrFromSStxPkScrCommitment(txOut.PkScript,
					idx.chainParams)
				if err != nil {
					// Ignore unsupported address types.
					continue
				}

				addrs = append(addrs, addr)
			}

			for _, addr := range addrs {
				k, err := addrToKey(addr, idx.chainParams)
				if err != nil {
					// Ignore unsupported address types.
					continue
				}

				usedAddrs[k] = struct{}{}
			}
		}
	}

	// Write all the newly used addresses to the database,
	// skipping any keys that already exist. Write any
	// addresses we see in mempool at this time, too,
	// then remove them from the unconfirmed map drop
	// dropping the old map and reassigning a new map.
	idx.unconfirmedLock.Lock()
	for k := range idx.mpExistsAddr {
		usedAddrs[k] = struct{}{}
	}
	idx.mpExistsAddr = make(map[[addrKeySize]byte]struct{})
	idx.unconfirmedLock.Unlock()

	meta := dbTx.Metadata()
	existsAddrIndex := meta.Bucket(existsAddrIndexKey)
	newUsedAddrs := make(map[[addrKeySize]byte]struct{})
	for k := range usedAddrs {
		if !idx.existsAddress(existsAddrIndex, k) {
			newUsedAddrs[k] = struct{}{}
		}
	}

	for k := range newUsedAddrs {
		err := dbPutExistsAddr(existsAddrIndex, k)
		if err != nil {
			return err
		}
	}

	return nil
}
Example #20
0
// IsCheckpointCandidate returns whether or not the passed block is a good
// checkpoint candidate.
//
// The factors used to determine a good checkpoint are:
//  - The block must be in the main chain
//  - The block must be at least 'CheckpointConfirmations' blocks prior to the
//    current end of the main chain
//  - The timestamps for the blocks before and after the checkpoint must have
//    timestamps which are also before and after the checkpoint, respectively
//    (due to the median time allowance this is not always the case)
//  - The block must not contain any strange transaction such as those with
//    nonstandard scripts
//
// The intent is that candidates are reviewed by a developer to make the final
// decision and then manually added to the list of checkpoints for a network.
func (b *BlockChain) IsCheckpointCandidate(block *dcrutil.Block) (bool, error) {
	// Checkpoints must be enabled.
	if b.noCheckpoints {
		return false, fmt.Errorf("checkpoints are disabled")
	}

	// A checkpoint must be in the main chain.
	exists, err := b.db.ExistsSha(block.Sha())
	if err != nil {
		return false, err
	}
	if !exists {
		return false, nil
	}

	// A checkpoint must be at least CheckpointConfirmations blocks before
	// the end of the main chain.
	blockHeight := block.Height()
	_, mainChainHeight, err := b.db.NewestSha()
	if err != nil {
		return false, err
	}
	if blockHeight > (mainChainHeight - CheckpointConfirmations) {
		return false, nil
	}

	// Get the previous block.
	prevHash := &block.MsgBlock().Header.PrevBlock
	prevBlock, err := b.db.FetchBlockBySha(prevHash)
	if err != nil {
		return false, err
	}

	// Get the next block.
	nextHash, err := b.db.FetchBlockShaByHeight(blockHeight + 1)
	if err != nil {
		return false, err
	}
	nextBlock, err := b.db.FetchBlockBySha(nextHash)
	if err != nil {
		return false, err
	}

	// A checkpoint must have timestamps for the block and the blocks on
	// either side of it in order (due to the median time allowance this is
	// not always the case).
	prevTime := prevBlock.MsgBlock().Header.Timestamp
	curTime := block.MsgBlock().Header.Timestamp
	nextTime := nextBlock.MsgBlock().Header.Timestamp
	if prevTime.After(curTime) || nextTime.Before(curTime) {
		return false, nil
	}

	// A checkpoint must have transactions that only contain standard
	// scripts.
	for _, tx := range block.Transactions() {
		if isNonstandardTransaction(tx) {
			return false, nil
		}
	}

	return true, nil
}
Example #21
0
// fetchInputUtxos loads utxo details about the input transactions referenced
// by the transactions in the given block into the view from the database as
// needed.  In particular, referenced entries that are earlier in the block are
// added to the view and entries that are already in the view are not modified.
func (view *UtxoViewpoint) fetchInputUtxos(db database.DB,
	block, parent *dcrutil.Block) error {
	viewpoint := view.StakeViewpoint()

	// 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.
	txInFlight := map[chainhash.Hash]int{}
	txNeededSet := make(map[chainhash.Hash]struct{})

	// Case 1: ViewpointPrevValidInitial. We need the viewpoint of the
	// current chain without the TxTreeRegular of the previous block
	// added so we can validate that.
	if viewpoint == ViewpointPrevValidInitial {
		transactions := parent.Transactions()
		for i, tx := range transactions {
			txInFlight[*tx.Sha()] = i
		}

		// Loop through all of the transaction inputs (except for the coinbase
		// which has no inputs) collecting them into sets of what is needed and
		// what is already known (in-flight).
		for i, tx := range transactions[1:] {
			for _, txIn := range tx.MsgTx().TxIn {
				// 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.  Add the outputs of the
				// referenced transaction as available utxos when this
				// is the case.  Otherwise, the utxo details are still
				// needed.
				//
				// NOTE: The >= is correct here because i is one less
				// than the actual position of the transaction within
				// the block due to skipping the coinbase.
				originHash := &txIn.PreviousOutPoint.Hash
				if inFlightIndex, ok := txInFlight[*originHash]; ok &&
					i >= inFlightIndex {

					originTx := transactions[inFlightIndex]
					view.AddTxOuts(originTx, block.Height(), uint32(i))
					continue
				}

				// Don't request entries that are already in the view
				// from the database.
				if _, ok := view.entries[*originHash]; ok {
					continue
				}

				txNeededSet[*originHash] = struct{}{}
			}
		}

		// Request the input utxos from the database.
		return view.fetchUtxosMain(db, txNeededSet)
	}

	// Case 2+3: ViewpointPrevValidStake and ViewpointPrevValidStake.
	// For ViewpointPrevValidStake, we need the viewpoint of the
	// current chain with the TxTreeRegular of the previous block
	// added so we can validate the TxTreeStake of the current block.
	// For ViewpointPrevInvalidStake, we need the viewpoint of the
	// current chain with the TxTreeRegular of the previous block
	// missing so we can validate the TxTreeStake of the current block.
	if viewpoint == ViewpointPrevValidStake ||
		viewpoint == ViewpointPrevInvalidStake {
		// We need all of the stake tx txins. None of these are considered
		// in-flight in relation to the regular tx tree or to other tx in
		// the stake tx tree, so don't do any of those expensive checks and
		// just append it to the tx slice.
		for _, tx := range block.MsgBlock().STransactions {
			isSSGen, _ := stake.IsSSGen(tx)

			for i, txIn := range tx.TxIn {
				// Ignore stakebases.
				if isSSGen && i == 0 {
					continue
				}

				// Add an entry to the transaction store for the needed
				// transaction with it set to missing by default.
				originHash := &txIn.PreviousOutPoint.Hash

				// Don't request entries that are already in the view
				// from the database.
				if _, ok := view.entries[*originHash]; ok {
					continue
				}

				txNeededSet[*originHash] = struct{}{}
			}
		}

		// Request the input utxos from the database.
		return view.fetchUtxosMain(db, txNeededSet)
	}

	// Case 4+5: ViewpointPrevValidRegular and
	// ViewpointPrevInvalidRegular.
	// For ViewpointPrevValidRegular, we need the viewpoint of the
	// current chain with the TxTreeRegular of the previous block
	// and the TxTreeStake of the current block added so we can
	// validate the TxTreeRegular of the current block.
	// For ViewpointPrevInvalidRegular, we need the viewpoint of the
	// current chain with the TxTreeRegular of the previous block
	// missing and the TxTreeStake of the current block added so we
	// can validate the TxTreeRegular of the current block.
	if viewpoint == ViewpointPrevValidRegular ||
		viewpoint == ViewpointPrevInvalidRegular {
		transactions := block.Transactions()
		for i, tx := range transactions {
			txInFlight[*tx.Sha()] = i
		}

		// Loop through all of the transaction inputs (except for the coinbase
		// which has no inputs) collecting them into sets of what is needed and
		// what is already known (in-flight).
		txNeededSet := make(map[chainhash.Hash]struct{})
		for i, tx := range transactions[1:] {
			for _, txIn := range tx.MsgTx().TxIn {
				// 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.  Add the outputs of the
				// referenced transaction as available utxos when this
				// is the case.  Otherwise, the utxo details are still
				// needed.
				//
				// NOTE: The >= is correct here because i is one less
				// than the actual position of the transaction within
				// the block due to skipping the coinbase.
				originHash := &txIn.PreviousOutPoint.Hash
				if inFlightIndex, ok := txInFlight[*originHash]; ok &&
					i >= inFlightIndex {

					originTx := transactions[inFlightIndex]
					view.AddTxOuts(originTx, block.Height(), uint32(i))
					continue
				}

				// Don't request entries that are already in the view
				// from the database.
				if _, ok := view.entries[*originHash]; ok {
					continue
				}

				txNeededSet[*originHash] = struct{}{}
			}
		}

		// Request the input utxos from the database.
		return view.fetchUtxosMain(db, txNeededSet)
	}

	// TODO actual blockchain error
	return fmt.Errorf("invalid stake viewpoint")
}
Example #22
0
// indexBlockAddrs returns a populated index of the all the transactions in the
// passed block based on the addresses involved in each transaction.
func (a *addrIndexer) indexBlockAddrs(blk *dcrutil.Block,
	parent *dcrutil.Block) (database.BlockAddrIndex, error) {
	var addrIndex database.BlockAddrIndex
	_, stxLocs, err := blk.TxLoc()
	if err != nil {
		return nil, err
	}

	txTreeRegularValid := dcrutil.IsFlagSet16(blk.MsgBlock().Header.VoteBits,
		dcrutil.BlockValid)

	// Add regular transactions iff the block was validated.
	if txTreeRegularValid {
		txLocs, _, err := parent.TxLoc()
		if err != nil {
			return nil, err
		}
		for txIdx, tx := range parent.Transactions() {
			// Tx's offset and length in the block.
			locInBlock := &txLocs[txIdx]

			// Coinbases don't have any inputs.
			if !blockchain.IsCoinBase(tx) {
				// Index the SPK's of each input's previous outpoint
				// transaction.
				for _, txIn := range tx.MsgTx().TxIn {
					prevOutTx, err := a.lookupTransaction(
						txIn.PreviousOutPoint.Hash,
						blk,
						parent)
					inputOutPoint := prevOutTx.TxOut[txIn.PreviousOutPoint.Index]

					toAppend, err := convertToAddrIndex(inputOutPoint.Version,
						inputOutPoint.PkScript, parent.Height(), locInBlock)
					if err != nil {
						adxrLog.Tracef("Error converting tx txin %v: %v",
							txIn.PreviousOutPoint.Hash, err)
						continue
					}
					addrIndex = append(addrIndex, toAppend...)
				}
			}

			for _, txOut := range tx.MsgTx().TxOut {
				toAppend, err := convertToAddrIndex(txOut.Version, txOut.PkScript,
					parent.Height(), locInBlock)
				if err != nil {
					adxrLog.Tracef("Error converting tx txout %v: %v",
						tx.MsgTx().TxSha(), err)
					continue
				}
				addrIndex = append(addrIndex, toAppend...)
			}
		}
	}

	// Add stake transactions.
	for stxIdx, stx := range blk.STransactions() {
		// Tx's offset and length in the block.
		locInBlock := &stxLocs[stxIdx]

		isSSGen, _ := stake.IsSSGen(stx)

		// Index the SPK's of each input's previous outpoint
		// transaction.
		for i, txIn := range stx.MsgTx().TxIn {
			// Stakebases don't have any inputs.
			if isSSGen && i == 0 {
				continue
			}

			// Lookup and fetch the referenced output's tx.
			prevOutTx, err := a.lookupTransaction(
				txIn.PreviousOutPoint.Hash,
				blk,
				parent)
			inputOutPoint := prevOutTx.TxOut[txIn.PreviousOutPoint.Index]

			toAppend, err := convertToAddrIndex(inputOutPoint.Version,
				inputOutPoint.PkScript, blk.Height(), locInBlock)
			if err != nil {
				adxrLog.Tracef("Error converting stx txin %v: %v",
					txIn.PreviousOutPoint.Hash, err)
				continue
			}
			addrIndex = append(addrIndex, toAppend...)
		}

		for _, txOut := range stx.MsgTx().TxOut {
			toAppend, err := convertToAddrIndex(txOut.Version, txOut.PkScript,
				blk.Height(), locInBlock)
			if err != nil {
				adxrLog.Tracef("Error converting stx txout %v: %v",
					stx.MsgTx().TxSha(), err)
				continue
			}
			addrIndex = append(addrIndex, toAppend...)
		}
	}

	return addrIndex, nil
}
Example #23
0
func connectTransactions(txStore TxStore, block *dcrutil.Block, parent *dcrutil.Block) error {
	// There is no regular tx from before the genesis block, so ignore the genesis
	// block for the next step.
	if parent != nil && block.Height() != 0 {
		mBlock := block.MsgBlock()
		votebits := mBlock.Header.VoteBits
		regularTxTreeValid := dcrutil.IsFlagSet16(votebits, dcrutil.BlockValid)

		// Only add the transactions in the event that the parent block's regular
		// tx were validated.
		if regularTxTreeValid {
			// Loop through all of the regular 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 parent.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() - 1
					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
					}
				}
			}
		}
	}

	// Loop through all of the stake 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 block.STransactions() {
		// 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 nil
}
Example #24
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
}
Example #25
0
// disconnectTransactions updates the passed map by undoing transaction and
// spend information for all transactions in the passed block.  Only
// transactions in the passed map are updated.
func disconnectTransactions(txStore TxStore, block *dcrutil.Block, parent *dcrutil.Block) error {
	// Loop through all of the stake transactions in the block to see if any of
	// them are ones that need to be undone based on the transaction store.
	for _, tx := range block.STransactions() {
		// Clear this transaction from the transaction store if needed.
		// Only clear it rather than deleting it because the transaction
		// connect code relies on its presence to decide whether or not
		// to update the store and any transactions which exist on both
		// sides of a fork would otherwise not be updated.
		if txD, exists := txStore[*tx.Sha()]; exists {
			txD.Tx = nil
			txD.BlockHeight = int64(wire.NullBlockHeight)
			txD.BlockIndex = wire.NullBlockIndex
			txD.Spent = nil
			txD.Err = database.ErrTxShaMissing
		}

		// Unspend the origin transaction output.
		for _, txIn := range tx.MsgTx().TxIn {
			originHash := &txIn.PreviousOutPoint.Hash
			originIndex := txIn.PreviousOutPoint.Index
			originTx, exists := txStore[*originHash]
			if exists && originTx.Tx != nil && originTx.Err == nil {
				if originTx.Spent == nil {
					continue
				}
				if originIndex >= uint32(len(originTx.Spent)) {
					continue
				}
				originTx.Spent[originIndex] = false
			}
		}
	}

	// There is no regular tx from before the genesis block, so ignore the genesis
	// block for the next step.
	if parent != nil && block.Height() != 0 {
		mBlock := block.MsgBlock()
		votebits := mBlock.Header.VoteBits
		regularTxTreeValid := dcrutil.IsFlagSet16(votebits, dcrutil.BlockValid)

		// Only bother to unspend transactions if the parent's tx tree was
		// validated. Otherwise, these transactions were never in the blockchain's
		// history in the first place.
		if regularTxTreeValid {
			// Loop through all of the regular transactions in the block to see if
			// any of them are ones that need to be undone based on the
			// transaction store.
			for _, tx := range parent.Transactions() {
				// Clear this transaction from the transaction store if needed.
				// Only clear it rather than deleting it because the transaction
				// connect code relies on its presence to decide whether or not
				// to update the store and any transactions which exist on both
				// sides of a fork would otherwise not be updated.
				if txD, exists := txStore[*tx.Sha()]; exists {
					txD.Tx = nil
					txD.BlockHeight = int64(wire.NullBlockHeight)
					txD.BlockIndex = wire.NullBlockIndex
					txD.Spent = nil
					txD.Err = database.ErrTxShaMissing
				}

				// Unspend the origin transaction output.
				for _, txIn := range tx.MsgTx().TxIn {
					originHash := &txIn.PreviousOutPoint.Hash
					originIndex := txIn.PreviousOutPoint.Index
					originTx, exists := txStore[*originHash]
					if exists && originTx.Tx != nil && originTx.Err == nil {
						if originTx.Spent == nil {
							continue
						}
						if originIndex >= uint32(len(originTx.Spent)) {
							continue
						}
						originTx.Spent[originIndex] = false
					}
				}
			}
		}
	}

	return nil
}
Example #26
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
}
Example #27
0
// fetchInputTransactions fetches the input transactions referenced by the
// transactions in the given block from its point of view.  See fetchTxList
// for more details on what the point of view entails.
// Decred: This function is for verifying the validity of the regular tx tree in
// 		this block for the case that it does get accepted in the next block.
func (b *BlockChain) fetchInputTransactions(node *blockNode, block *dcrutil.Block, viewpoint int8) (TxStore, error) {
	// Verify we have the same node as we do block.
	blockHash := block.Sha()
	if !node.hash.IsEqual(blockHash) {
		return nil, fmt.Errorf("node and block hash are different!")
	}

	// If we need the previous block, grab it.
	var parentBlock *dcrutil.Block
	if viewpoint == ViewpointPrevValidInitial ||
		viewpoint == ViewpointPrevValidStake ||
		viewpoint == ViewpointPrevValidRegular {
		var errFetchBlock error
		parentBlock, errFetchBlock = b.getBlockFromHash(node.parentHash)
		if errFetchBlock != nil {
			return nil, errFetchBlock
		}
	}

	txInFlight := map[chainhash.Hash]int{}
	txNeededSet := make(map[chainhash.Hash]struct{})
	txStore := make(TxStore)

	// Case 1: ViewpointPrevValidInitial. We need the viewpoint of the
	// current chain without the TxTreeRegular of the previous block
	// added so we can validate that.
	if viewpoint == ViewpointPrevValidInitial {
		// 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.
		transactions := parentBlock.Transactions()
		for i, tx := range transactions {
			txInFlight[*tx.Sha()] = i
		}

		// Loop through all of the transaction inputs (except for the coinbase
		// which has no inputs) collecting them into sets of what is needed and
		// what is already known (in-flight).
		for i, tx := range transactions[1:] {
			for _, txIn := range tx.MsgTx().TxIn {
				// Add an entry to the transaction store for the needed
				// transaction with it set to missing by default.
				originHash := &txIn.PreviousOutPoint.Hash
				txD := &TxData{Hash: originHash, Err: database.ErrTxShaMissing}
				txStore[*originHash] = txD

				// 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.  Update the transaction
				// store acccordingly when this is the case.  Otherwise,
				// we still need the transaction.
				//
				// NOTE: The >= is correct here because i is one less
				// than the actual position of the transaction within
				// the block due to skipping the coinbase.
				if inFlightIndex, ok := txInFlight[*originHash]; ok &&
					i >= inFlightIndex {

					originTx := transactions[inFlightIndex]
					txD.Tx = originTx
					txD.BlockHeight = node.height - 1
					txD.BlockIndex = uint32(inFlightIndex)
					txD.Spent = make([]bool, len(originTx.MsgTx().TxOut))
					txD.Err = nil
				} else {
					txNeededSet[*originHash] = struct{}{}
				}
			}
		}

		// Request the input transactions from the point of view of the node.
		txNeededStore, err := b.fetchTxStore(node, block, txNeededSet, viewpoint)
		if err != nil {
			return nil, err
		}

		// Merge the results of the requested transactions and the in-flight
		// transactions.
		for _, txD := range txNeededStore {
			txStore[*txD.Hash] = txD
		}

		return txStore, nil
	}

	// Case 2+3: ViewpointPrevValidStake and ViewpointPrevValidStake.
	// For ViewpointPrevValidStake, we need the viewpoint of the
	// current chain with the TxTreeRegular of the previous block
	// added so we can validate the TxTreeStake of the current block.
	// For ViewpointPrevInvalidStake, we need the viewpoint of the
	// current chain with the TxTreeRegular of the previous block
	// missing so we can validate the TxTreeStake of the current block.
	if viewpoint == ViewpointPrevValidStake ||
		viewpoint == ViewpointPrevInvalidStake {
		// We need all of the stake tx txins. None of these are considered
		// in-flight in relation to the regular tx tree or to other tx in
		// the stake tx tree, so don't do any of those expensive checks and
		// just append it to the tx slice.
		stransactions := block.STransactions()
		for _, tx := range stransactions {
			isSSGen, _ := stake.IsSSGen(tx)

			for i, txIn := range tx.MsgTx().TxIn {
				// Ignore stakebases.
				if isSSGen && i == 0 {
					continue
				}

				// Add an entry to the transaction store for the needed
				// transaction with it set to missing by default.
				originHash := &txIn.PreviousOutPoint.Hash
				txD := &TxData{Hash: originHash, Err: database.ErrTxShaMissing}
				txStore[*originHash] = txD

				txNeededSet[*originHash] = struct{}{}
			}
		}

		// Request the input transactions from the point of view of the node.
		txNeededStore, err := b.fetchTxStore(node, block, txNeededSet, viewpoint)
		if err != nil {
			return nil, err
		}

		return txNeededStore, nil
	}

	// Case 4+5: ViewpointPrevValidRegular and
	// ViewpointPrevInvalidRegular.
	// For ViewpointPrevValidRegular, we need the viewpoint of the
	// current chain with the TxTreeRegular of the previous block
	// and the TxTreeStake of the current block added so we can
	// validate the TxTreeRegular of the current block.
	// For ViewpointPrevInvalidRegular, we need the viewpoint of the
	// current chain with the TxTreeRegular of the previous block
	// missing and the TxTreeStake of the current block added so we
	// can validate the TxTreeRegular of the current block.
	if viewpoint == ViewpointPrevValidRegular ||
		viewpoint == ViewpointPrevInvalidRegular {
		transactions := block.Transactions()
		for i, tx := range transactions {
			txInFlight[*tx.Sha()] = i
		}

		// Loop through all of the transaction inputs (except for the coinbase
		// which has no inputs) collecting them into sets of what is needed and
		// what is already known (in-flight).
		txNeededSet := make(map[chainhash.Hash]struct{})
		txStore = make(TxStore)
		for i, tx := range transactions[1:] {
			for _, txIn := range tx.MsgTx().TxIn {
				// Add an entry to the transaction store for the needed
				// transaction with it set to missing by default.
				originHash := &txIn.PreviousOutPoint.Hash
				txD := &TxData{Hash: originHash, Err: database.ErrTxShaMissing}
				txStore[*originHash] = txD

				// 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.  Update the transaction
				// store acccordingly when this is the case.  Otherwise,
				// we still need the transaction.
				//
				// NOTE: The >= is correct here because i is one less
				// than the actual position of the transaction within
				// the block due to skipping the coinbase.
				if inFlightIndex, ok := txInFlight[*originHash]; ok &&
					i >= inFlightIndex {

					originTx := transactions[inFlightIndex]
					txD.Tx = originTx
					txD.BlockHeight = node.height
					txD.BlockIndex = uint32(inFlightIndex)
					txD.Spent = make([]bool, len(originTx.MsgTx().TxOut))
					txD.Err = nil
				} else {
					txNeededSet[*originHash] = struct{}{}
				}
			}
		}

		// Request the input transactions from the point of view of the node.
		txNeededStore, err := b.fetchTxStore(node, block, txNeededSet, viewpoint)
		if err != nil {
			return nil, err
		}

		// Merge the results of the requested transactions and the in-flight
		// transactions.
		for _, txD := range txNeededStore {
			txStore[*txD.Hash] = txD
		}

		return txStore, nil
	}

	return nil, fmt.Errorf("Invalid viewpoint passed to fetchInputTransactions")
}
Example #28
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
}
Example #29
0
// indexBlock extract all of the standard addresses from all of the transactions
// in the passed block and maps each of them to the assocaited transaction using
// the passed map.
func (idx *AddrIndex) indexBlock(data writeIndexData, block, parent *dcrutil.Block, view *blockchain.UtxoViewpoint) {
	regularTxTreeValid := dcrutil.IsFlagSet16(block.MsgBlock().Header.VoteBits,
		dcrutil.BlockValid)
	var stakeStartIdx int
	if regularTxTreeValid {
		for txIdx, tx := range parent.Transactions() {
			// Coinbases do not reference any inputs.  Since the block is
			// required to have already gone through full validation, it has
			// already been proven on the first transaction in the block is
			// a coinbase.
			if txIdx != 0 {
				for _, txIn := range tx.MsgTx().TxIn {
					// The view should always have the input since
					// the index contract requires it, however, be
					// safe and simply ignore any missing entries.
					origin := &txIn.PreviousOutPoint
					entry := view.LookupEntry(&origin.Hash)
					if entry == nil {
						log.Warnf("Missing input %v for tx %v while "+
							"indexing block %v (height %v)\n", origin.Hash,
							tx.Sha(), block.Sha(), block.Height())
						continue
					}

					version := entry.ScriptVersionByIndex(origin.Index)
					pkScript := entry.PkScriptByIndex(origin.Index)
					txType := entry.TransactionType()
					idx.indexPkScript(data, version, pkScript, txIdx,
						txType == stake.TxTypeSStx)
				}
			}

			for _, txOut := range tx.MsgTx().TxOut {
				idx.indexPkScript(data, txOut.Version, txOut.PkScript, txIdx,
					false)
			}
		}

		stakeStartIdx = len(parent.Transactions())
	}

	for txIdx, tx := range block.STransactions() {
		msgTx := tx.MsgTx()
		thisTxOffset := txIdx + stakeStartIdx

		isSSGen, _ := stake.IsSSGen(msgTx)
		for i, txIn := range msgTx.TxIn {
			// Skip stakebases.
			if isSSGen && i == 0 {
				continue
			}

			// The view should always have the input since
			// the index contract requires it, however, be
			// safe and simply ignore any missing entries.
			origin := &txIn.PreviousOutPoint
			entry := view.LookupEntry(&origin.Hash)
			if entry == nil {
				log.Warnf("Missing input %v for tx %v while "+
					"indexing block %v (height %v)\n", origin.Hash,
					tx.Sha(), block.Sha(), block.Height())
				continue
			}

			version := entry.ScriptVersionByIndex(origin.Index)
			pkScript := entry.PkScriptByIndex(origin.Index)
			txType := entry.TransactionType()
			idx.indexPkScript(data, version, pkScript, thisTxOffset,
				txType == stake.TxTypeSStx)
		}

		isSStx, _ := stake.IsSStx(msgTx)
		for _, txOut := range msgTx.TxOut {
			idx.indexPkScript(data, txOut.Version, txOut.PkScript,
				thisTxOffset, isSStx)
		}
	}
}