Example #1
0
// FetchTransactionStore fetches the input transactions referenced by the
// passed transaction from the point of view of the end of the main chain.  It
// also attempts to fetch the transaction itself so the returned TxStore can be
// examined for duplicate transactions.
// IsValid indicates if the current block on head has had its TxTreeRegular
// validated by the stake voters.
func (b *BlockChain) FetchTransactionStore(tx *dcrutil.Tx,
	isValid bool) (TxStore, error) {
	isSSGen, _ := stake.IsSSGen(tx)

	// Create a set of needed transactions from the transactions referenced
	// by the inputs of the passed transaction.  Also, add the passed
	// transaction itself as a way for the caller to detect duplicates.
	txNeededSet := make(map[chainhash.Hash]struct{})
	txNeededSet[*tx.Sha()] = struct{}{}
	for i, txIn := range tx.MsgTx().TxIn {
		// Skip all stakebase inputs.
		if isSSGen && (i == 0) {
			continue
		}

		txNeededSet[txIn.PreviousOutPoint.Hash] = struct{}{}
	}

	// Request the input transactions from the point of view of the end of
	// the main chain without including fully spent transactions in the
	// results.  Fully spent transactions are only needed for chain
	// reorganization which does not apply here.
	txStore := fetchTxStoreMain(b.db, txNeededSet, false)

	topBlock, err := b.getBlockFromHash(b.bestChain.hash)
	if err != nil {
		return nil, err
	}

	if isValid {
		connectTxTree(txStore, topBlock, true)
	}

	return txStore, nil
}
Example #2
0
// matchTxAndUpdate returns true if the bloom filter matches data within the
// passed transaction, otherwise false is returned.  If the filter does match
// the passed transaction, it will also update the filter depending on the bloom
// update flags set via the loaded filter if needed.
//
// This function MUST be called with the filter lock held.
func (bf *Filter) matchTxAndUpdate(tx *dcrutil.Tx) bool {
	// Check if the filter matches the hash of the transaction.
	// This is useful for finding transactions when they appear in a block.
	matched := bf.matches(tx.Sha().Bytes())

	// Check if the filter matches any data elements in the public key
	// scripts of any of the outputs.  When it does, add the outpoint that
	// matched so transactions which spend from the matched transaction are
	// also included in the filter.  This removes the burden of updating the
	// filter for this scenario from the client.  It is also more efficient
	// on the network since it avoids the need for another filteradd message
	// from the client and avoids some potential races that could otherwise
	// occur.
	for i, txOut := range tx.MsgTx().TxOut {
		pushedData, err := txscript.PushedData(txOut.PkScript)
		if err != nil {
			continue
		}

		for _, data := range pushedData {
			if !bf.matches(data) {
				continue
			}

			matched = true
			bf.maybeAddOutpoint(txOut.Version, txOut.PkScript, tx.Sha(),
				uint32(i), tx.Tree())
			break
		}
	}

	// Nothing more to do if a match has already been made.
	if matched {
		return true
	}

	// At this point, the transaction and none of the data elements in the
	// public key scripts of its outputs matched.

	// Check if the filter matches any outpoints this transaction spends or
	// any any data elements in the signature scripts of any of the inputs.
	for _, txin := range tx.MsgTx().TxIn {
		if bf.matchesOutPoint(&txin.PreviousOutPoint) {
			return true
		}

		pushedData, err := txscript.PushedData(txin.SignatureScript)
		if err != nil {
			continue
		}
		for _, data := range pushedData {
			if bf.matches(data) {
				return true
			}
		}
	}

	return false
}
Example #3
0
// AddTxOuts adds all outputs in the passed transaction which are not provably
// unspendable to the view.  When the view already has entries for any of the
// outputs, they are simply marked unspent.  All fields will be updated for
// existing entries since it's possible it has changed during a reorg.
func (view *UtxoViewpoint) AddTxOuts(tx *dcrutil.Tx, blockHeight int64,
	blockIndex uint32) {
	msgTx := tx.MsgTx()
	// When there are not already any utxos associated with the transaction,
	// add a new entry for it to the view.
	entry := view.LookupEntry(tx.Sha())
	if entry == nil {
		txType := stake.DetermineTxType(msgTx)
		entry = newUtxoEntry(msgTx.Version, uint32(blockHeight),
			blockIndex, IsCoinBaseTx(msgTx), msgTx.Expiry != 0, txType)
		if txType == stake.TxTypeSStx {
			stakeExtra := make([]byte, serializeSizeForMinimalOutputs(tx))
			putTxToMinimalOutputs(stakeExtra, tx)
			entry.stakeExtra = stakeExtra
		}
		view.entries[*tx.Sha()] = entry
	} else {
		entry.height = uint32(blockHeight)
		entry.index = uint32(blockIndex)
	}
	entry.modified = true

	// Loop all of the transaction outputs and add those which are not
	// provably unspendable.
	for txOutIdx, txOut := range tx.MsgTx().TxOut {
		// TODO allow pruning of stake utxs after all other outputs are spent
		if txscript.IsUnspendable(txOut.Value, txOut.PkScript) {
			continue
		}

		// Update existing entries.  All fields are updated because it's
		// possible (although extremely unlikely) that the existing
		// entry is being replaced by a different transaction with the
		// same hash.  This is allowed so long as the previous
		// transaction is fully spent.
		if output, ok := entry.sparseOutputs[uint32(txOutIdx)]; ok {
			output.spent = false
			output.amount = txOut.Value
			output.scriptVersion = txOut.Version
			output.pkScript = txOut.PkScript
			output.compressed = false
			continue
		}

		// Add the unspent transaction output.
		entry.sparseOutputs[uint32(txOutIdx)] = &utxoOutput{
			spent:         false,
			amount:        txOut.Value,
			scriptVersion: txOut.Version,
			pkScript:      txOut.PkScript,
			compressed:    false,
		}
	}
	return
}
Example #4
0
// FetchUtxoView loads utxo details about the input transactions referenced by
// the passed transaction from the point of view of the end of the main chain.
// It also attempts to fetch the utxo details for the transaction itself so the
// returned view can be examined for duplicate unspent transaction outputs.
//
// This function is safe for concurrent access however the returned view is NOT.
func (b *BlockChain) FetchUtxoView(tx *dcrutil.Tx, treeValid bool) (*UtxoViewpoint,
	error) {
	b.chainLock.RLock()
	defer b.chainLock.RUnlock()

	// Request the utxos from the point of view of the end of the main
	// chain.
	view := NewUtxoViewpoint()
	if treeValid {
		view.SetStakeViewpoint(ViewpointPrevValidRegular)
		block, err := b.fetchBlockFromHash(&b.bestNode.hash)
		if err != nil {
			return nil, err
		}
		parent, err := b.fetchBlockFromHash(&b.bestNode.header.PrevBlock)
		if err != nil {
			return nil, err
		}
		err = view.fetchInputUtxos(b.db, block, parent)
		if err != nil {
			return nil, err
		}
		for i, blockTx := range block.Transactions() {
			err := view.connectTransaction(blockTx, b.bestNode.height,
				uint32(i), nil)
			if err != nil {
				return nil, err
			}
		}
	}
	view.SetBestHash(&b.bestNode.hash)

	// Create a set of needed transactions based on those referenced by the
	// inputs of the passed transaction.  Also, add the passed transaction
	// itself as a way for the caller to detect duplicates that are not
	// fully spent.
	txNeededSet := make(map[chainhash.Hash]struct{})
	txNeededSet[*tx.Sha()] = struct{}{}
	msgTx := tx.MsgTx()
	isSSGen, _ := stake.IsSSGen(msgTx)
	if !IsCoinBaseTx(msgTx) {
		for i, txIn := range msgTx.TxIn {
			if isSSGen && i == 0 {
				continue
			}
			txNeededSet[txIn.PreviousOutPoint.Hash] = struct{}{}
		}
	}

	err := view.fetchUtxosMain(b.db, txNeededSet)

	return view, err
}
Example #5
0
// insertSStx inserts an SStx into the store.
func (s *StakeStore) insertSStx(ns walletdb.ReadWriteBucket, sstx *dcrutil.Tx, voteBits stake.VoteBits) error {
	// If we already have the SStx, no need to
	// try to include twice.
	exists := s.checkHashInStore(sstx.Sha())
	if exists {
		log.Tracef("Attempted to insert SStx %v into the stake store, "+
			"but the SStx already exists.", sstx.Sha())
		return nil
	}
	record := &sstxRecord{
		sstx,
		time.Now(),
		true,
		voteBits.Bits,
		voteBits.ExtendedBits,
	}

	// Add the SStx to the database.
	err := putSStxRecord(ns, record, voteBits)
	if err != nil {
		return err
	}

	// Add the SStx's hash to the internal list in the store.
	s.addHashToStore(sstx.Sha())

	return nil
}
Example #6
0
// insertSStx inserts an SStx into the store.
func (s *StakeStore) insertSStx(sstx *dcrutil.Tx) error {
	// If we already have the SStx, no need to
	// try to include twice.
	exists := s.checkHashInStore(sstx.Sha())
	if exists {
		log.Tracef("Attempted to insert SStx %v into the stake store, "+
			"but the SStx already exists.", sstx.Sha())
		return nil
	}
	record := &sstxRecord{
		sstx,
		time.Now(),
	}

	// Add the SStx to the database.
	err := s.namespace.Update(func(tx walletdb.Tx) error {
		if putErr := putSStxRecord(tx, record); putErr != nil {
			return putErr
		}

		return nil
	})
	if err != nil {
		return err
	}

	// Add the SStx's hash to the internal list in the store.
	s.addHashToStore(sstx.Sha())

	return nil
}
Example #7
0
// indexUnconfirmedAddresses modifies the unconfirmed (memory-only) address
// index to include mappings for the addresses encoded by the passed public key
// script to the transaction.
//
// This function is safe for concurrent access.
func (idx *AddrIndex) indexUnconfirmedAddresses(scriptVersion uint16, pkScript []byte, tx *dcrutil.Tx, isSStx bool) {
	// The error is ignored here since the only reason it can fail is if the
	// script fails to parse and it was already validated before being
	// admitted to the mempool.
	class, addresses, _, _ := txscript.ExtractPkScriptAddrs(scriptVersion,
		pkScript, idx.chainParams)

	if isSStx && class == txscript.NullDataTy {
		addr, err := stake.AddrFromSStxPkScrCommitment(pkScript, idx.chainParams)
		if err != nil {
			// Fail if this fails to decode. It should.
			return
		}

		addresses = append(addresses, addr)
	}

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

		// Add a mapping from the address to the transaction.
		idx.unconfirmedLock.Lock()
		addrIndexEntry := idx.txnsByAddr[addrKey]
		if addrIndexEntry == nil {
			addrIndexEntry = make(map[chainhash.Hash]*dcrutil.Tx)
			idx.txnsByAddr[addrKey] = addrIndexEntry
		}
		addrIndexEntry[*tx.Sha()] = tx

		// Add a mapping from the transaction to the address.
		addrsByTxEntry := idx.addrsByTx[*tx.Sha()]
		if addrsByTxEntry == nil {
			addrsByTxEntry = make(map[[addrKeySize]byte]struct{})
			idx.addrsByTx[*tx.Sha()] = addrsByTxEntry
		}
		addrsByTxEntry[addrKey] = struct{}{}
		idx.unconfirmedLock.Unlock()
	}
}