Exemplo n.º 1
0
// AddUnconfirmedTx adds all addresses related to the transaction to the
// unconfirmed (memory-only) address index.
//
// NOTE: This transaction MUST have already been validated by the memory pool
// before calling this function with it and have all of the inputs available in
// the provided utxo view.  Failure to do so could result in some or all
// addresses not being indexed.
//
// This function is safe for concurrent access.
func (idx *AddrIndex) AddUnconfirmedTx(tx *dcrutil.Tx, utxoView *blockchain.UtxoViewpoint) {
	// Index addresses of all referenced previous transaction outputs.
	//
	// The existence checks are elided since this is only called after the
	// transaction has already been validated and thus all inputs are
	// already known to exist.
	msgTx := tx.MsgTx()
	isSSGen, _ := stake.IsSSGen(msgTx)
	for i, txIn := range msgTx.TxIn {
		// Skip stakebase.
		if i == 0 && isSSGen {
			continue
		}

		entry := utxoView.LookupEntry(&txIn.PreviousOutPoint.Hash)
		if entry == nil {
			// Ignore missing entries.  This should never happen
			// in practice since the function comments specifically
			// call out all inputs must be available.
			continue
		}
		version := entry.ScriptVersionByIndex(txIn.PreviousOutPoint.Index)
		pkScript := entry.PkScriptByIndex(txIn.PreviousOutPoint.Index)
		txType := entry.TransactionType()
		idx.indexUnconfirmedAddresses(version, pkScript, tx,
			txType == stake.TxTypeSStx)
	}

	// Index addresses of all created outputs.
	isSStx, _ := stake.IsSStx(msgTx)
	for _, txOut := range msgTx.TxOut {
		idx.indexUnconfirmedAddresses(txOut.Version, txOut.PkScript, tx,
			isSStx)
	}
}
Exemplo n.º 2
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
}
Exemplo n.º 3
0
// voteVersionsInBlock returns all versions in a block.
func voteVersionsInBlock(bl *dcrutil.Block, params *chaincfg.Params) []uint32 {
	versions := make([]uint32, 0, params.TicketsPerBlock)
	for _, stx := range bl.MsgBlock().STransactions {
		if is, _ := stake.IsSSGen(stx); !is {
			continue
		}
		versions = append(versions, stake.SSGenVersion(stx))
	}

	return versions
}
Exemplo n.º 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
}
Exemplo n.º 5
0
// CalculateAddedSubsidy calculates the amount of subsidy added by a block
// and its parent. The blocks passed to this function MUST be valid blocks
// that have already been confirmed to abide by the consensus rules of the
// network, or the function might panic.
func CalculateAddedSubsidy(block, parent *dcrutil.Block) int64 {
	var subsidy int64

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

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

	return subsidy
}
Exemplo n.º 6
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
}
Exemplo n.º 7
0
// DebugMsgTxString dumps a verbose message containing information about the
// contents of a transaction.
func DebugMsgTxString(msgTx *wire.MsgTx) string {
	tx := dcrutil.NewTx(msgTx)
	isSStx, _ := stake.IsSStx(tx)
	isSSGen, _ := stake.IsSSGen(tx)
	var sstxType []bool
	var sstxPkhs [][]byte
	var sstxAmts []int64
	var sstxRules [][]bool
	var sstxLimits [][]uint16

	if isSStx {
		sstxType, sstxPkhs, sstxAmts, _, sstxRules, sstxLimits =
			stake.GetSStxStakeOutputInfo(tx)
	}

	var buffer bytes.Buffer

	hash := msgTx.TxSha()
	str := fmt.Sprintf("Transaction hash: %v, Version %v, Locktime: %v, "+
		"Expiry %v\n\n", hash, msgTx.Version, msgTx.LockTime, msgTx.Expiry)
	buffer.WriteString(str)

	str = fmt.Sprintf("==INPUTS==\nNumber of inputs: %v\n\n",
		len(msgTx.TxIn))
	buffer.WriteString(str)

	for i, input := range msgTx.TxIn {
		str = fmt.Sprintf("Input number: %v\n", i)
		buffer.WriteString(str)

		str = fmt.Sprintf("Previous outpoint hash: %v, ",
			input.PreviousOutPoint.Hash)
		buffer.WriteString(str)

		str = fmt.Sprintf("Previous outpoint index: %v, ",
			input.PreviousOutPoint.Index)
		buffer.WriteString(str)

		str = fmt.Sprintf("Previous outpoint tree: %v \n",
			input.PreviousOutPoint.Tree)
		buffer.WriteString(str)

		str = fmt.Sprintf("Sequence: %v \n",
			input.Sequence)
		buffer.WriteString(str)

		str = fmt.Sprintf("ValueIn: %v \n",
			input.ValueIn)
		buffer.WriteString(str)

		str = fmt.Sprintf("BlockHeight: %v \n",
			input.BlockHeight)
		buffer.WriteString(str)

		str = fmt.Sprintf("BlockIndex: %v \n",
			input.BlockIndex)
		buffer.WriteString(str)

		str = fmt.Sprintf("Raw signature script: %x \n", input.SignatureScript)
		buffer.WriteString(str)

		sigScr, _ := txscript.DisasmString(input.SignatureScript)
		str = fmt.Sprintf("Disasmed signature script: %v \n\n",
			sigScr)
		buffer.WriteString(str)
	}

	str = fmt.Sprintf("==OUTPUTS==\nNumber of outputs: %v\n\n",
		len(msgTx.TxOut))
	buffer.WriteString(str)

	for i, output := range msgTx.TxOut {
		str = fmt.Sprintf("Output number: %v\n", i)
		buffer.WriteString(str)

		coins := float64(output.Value) / 1e8
		str = fmt.Sprintf("Output amount: %v atoms or %v coins\n", output.Value,
			coins)
		buffer.WriteString(str)

		// SStx OP_RETURNs, dump pkhs and amts committed
		if isSStx && i != 0 && i%2 == 1 {
			coins := float64(sstxAmts[i/2]) / 1e8
			str = fmt.Sprintf("SStx commit amount: %v atoms or %v coins\n",
				sstxAmts[i/2], coins)
			buffer.WriteString(str)
			str = fmt.Sprintf("SStx commit address: %x\n",
				sstxPkhs[i/2])
			buffer.WriteString(str)
			str = fmt.Sprintf("SStx address type is P2SH: %v\n",
				sstxType[i/2])
			buffer.WriteString(str)

			str = fmt.Sprintf("SStx all address types is P2SH: %v\n",
				sstxType)
			buffer.WriteString(str)

			str = fmt.Sprintf("Voting is fee limited: %v\n",
				sstxLimits[i/2][0])
			buffer.WriteString(str)
			if sstxRules[i/2][0] {
				str = fmt.Sprintf("Voting limit imposed: %v\n",
					sstxLimits[i/2][0])
				buffer.WriteString(str)
			}

			str = fmt.Sprintf("Revoking is fee limited: %v\n",
				sstxRules[i/2][1])
			buffer.WriteString(str)

			if sstxRules[i/2][1] {
				str = fmt.Sprintf("Voting limit imposed: %v\n",
					sstxLimits[i/2][1])
				buffer.WriteString(str)
			}
		}

		// SSGen block/block height OP_RETURN.
		if isSSGen && i == 0 {
			blkHash, blkHeight, _ := stake.GetSSGenBlockVotedOn(tx)
			str = fmt.Sprintf("SSGen block hash voted on: %v, height: %v\n",
				blkHash, blkHeight)
			buffer.WriteString(str)
		}

		if isSSGen && i == 1 {
			vb := stake.GetSSGenVoteBits(tx)
			str = fmt.Sprintf("SSGen vote bits: %v\n", vb)
			buffer.WriteString(str)
		}

		str = fmt.Sprintf("Raw script: %x \n", output.PkScript)
		buffer.WriteString(str)

		scr, _ := txscript.DisasmString(output.PkScript)
		str = fmt.Sprintf("Disasmed script: %v \n\n", scr)
		buffer.WriteString(str)
	}

	return buffer.String()
}
Exemplo n.º 8
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")
}
Exemplo n.º 9
0
func (w *Wallet) addRelevantTx(dbtx walletdb.ReadWriteTx, rec *wtxmgr.TxRecord,
	block *wtxmgr.BlockMeta) error {

	addrmgrNs := dbtx.ReadWriteBucket(waddrmgrNamespaceKey)
	stakemgrNs := dbtx.ReadWriteBucket(wstakemgrNamespaceKey)
	txmgrNs := dbtx.ReadWriteBucket(wtxmgrNamespaceKey)

	// At the moment all notified transactions are assumed to actually be
	// relevant.  This assumption will not hold true when SPV support is
	// added, but until then, simply insert the transaction because there
	// should either be one or more relevant inputs or outputs.
	//
	// TODO This function is pretty bad corruption wise, it's very easy
	// to corrupt the wallet if you ctrl+c while in this function. This
	// needs desperate refactoring.

	tx := dcrutil.NewTx(&rec.MsgTx)
	txHash := rec.Hash

	// Handle incoming SStx; store them in the stake manager if we own
	// the OP_SSTX tagged out, except if we're operating as a stake pool
	// server. In that case, additionally consider the first commitment
	// output as well.
	if is, _ := stake.IsSStx(&rec.MsgTx); is {
		// Errors don't matter here.  If addrs is nil, the range below
		// does nothing.
		txOut := tx.MsgTx().TxOut[0]

		_, addrs, _, _ := txscript.ExtractPkScriptAddrs(txOut.Version,
			txOut.PkScript, w.chainParams)
		insert := false
		for _, addr := range addrs {
			_, err := w.Manager.Address(addrmgrNs, addr)
			if err == nil {
				// We own the voting output pubkey or script and we're
				// not operating as a stake pool, so simply insert this
				// ticket now.
				if !w.stakePoolEnabled {
					insert = true
					break
				} else {
					// We are operating as a stake pool. The below
					// function will ONLY add the ticket into the
					// stake pool if it has been found within a
					// block.
					if block == nil {
						break
					}

					valid, errEval := w.evaluateStakePoolTicket(rec, block,
						addr)
					if valid {
						// Be sure to insert this into the user's stake
						// pool entry into the stake manager.
						poolTicket := &wstakemgr.PoolTicket{
							Ticket:       txHash,
							HeightTicket: uint32(block.Height),
							Status:       wstakemgr.TSImmatureOrLive,
						}
						errUpdate := w.StakeMgr.UpdateStakePoolUserTickets(
							stakemgrNs, addrmgrNs, addr, poolTicket)
						if errUpdate != nil {
							log.Warnf("Failed to insert stake pool "+
								"user ticket: %s", err.Error())
						}
						log.Debugf("Inserted stake pool ticket %v for user %v "+
							"into the stake store database", txHash, addr)

						insert = true
						break
					}

					// Log errors if there were any. At this point the ticket
					// must be invalid, so insert it into the list of invalid
					// user tickets.
					if errEval != nil {
						log.Warnf("Ticket %v failed ticket evaluation for "+
							"the stake pool: %s", rec.Hash, err.Error())
					}
					errUpdate := w.StakeMgr.UpdateStakePoolUserInvalTickets(
						stakemgrNs, addr, &rec.Hash)
					if errUpdate != nil {
						log.Warnf("Failed to update pool user %v with "+
							"invalid ticket %v", addr.EncodeAddress(),
							rec.Hash)
					}
				}
			}
		}

		if insert {
			err := w.StakeMgr.InsertSStx(stakemgrNs, tx, w.VoteBits)
			if err != nil {
				log.Errorf("Failed to insert SStx %v"+
					"into the stake store.", tx.Sha())
			}
		}
	}

	// Handle incoming SSGen; store them if we own
	// the ticket used to purchase them.
	if is, _ := stake.IsSSGen(&rec.MsgTx); is {
		if block != nil {
			txInHash := tx.MsgTx().TxIn[1].PreviousOutPoint.Hash
			if w.StakeMgr.CheckHashInStore(&txInHash) {
				w.StakeMgr.InsertSSGen(stakemgrNs, &block.Hash,
					int64(block.Height),
					&txHash,
					w.VoteBits.Bits,
					&txInHash)
			}

			// If we're running as a stake pool, insert
			// the stake pool user ticket update too.
			if w.stakePoolEnabled {
				txInHeight := tx.MsgTx().TxIn[1].BlockHeight
				poolTicket := &wstakemgr.PoolTicket{
					Ticket:       txInHash,
					HeightTicket: txInHeight,
					Status:       wstakemgr.TSVoted,
					SpentBy:      txHash,
					HeightSpent:  uint32(block.Height),
				}

				poolUser, err := w.StakeMgr.SStxAddress(stakemgrNs, &txInHash)
				if err != nil {
					log.Warnf("Failed to fetch stake pool user for "+
						"ticket %v (voted ticket)", txInHash)
				} else {
					err = w.StakeMgr.UpdateStakePoolUserTickets(
						stakemgrNs, addrmgrNs, poolUser, poolTicket)
					if err != nil {
						log.Warnf("Failed to update stake pool ticket for "+
							"stake pool user %s after voting",
							poolUser.EncodeAddress())
					} else {
						log.Debugf("Updated voted stake pool ticket %v "+
							"for user %v into the stake store database ("+
							"vote hash: %v)", txInHash, poolUser, txHash)
					}
				}
			}
		} else {
			// If there's no associated block, it's potentially a
			// doublespent SSGen. Just ignore it and wait for it
			// to later get into a block.
			return nil
		}
	}

	// Handle incoming SSRtx; store them if we own
	// the ticket used to purchase them.
	if is, _ := stake.IsSSRtx(&rec.MsgTx); is {
		if block != nil {
			txInHash := tx.MsgTx().TxIn[0].PreviousOutPoint.Hash

			if w.StakeMgr.CheckHashInStore(&txInHash) {
				w.StakeMgr.InsertSSRtx(stakemgrNs, &block.Hash,
					int64(block.Height),
					&txHash,
					&txInHash)
			}

			// If we're running as a stake pool, insert
			// the stake pool user ticket update too.
			if w.stakePoolEnabled {
				txInHeight := tx.MsgTx().TxIn[0].BlockHeight
				poolTicket := &wstakemgr.PoolTicket{
					Ticket:       txInHash,
					HeightTicket: txInHeight,
					Status:       wstakemgr.TSMissed,
					SpentBy:      txHash,
					HeightSpent:  uint32(block.Height),
				}

				poolUser, err := w.StakeMgr.SStxAddress(stakemgrNs, &txInHash)
				if err != nil {
					log.Warnf("failed to fetch stake pool user for "+
						"ticket %v (missed ticket)", txInHash)
				} else {
					err = w.StakeMgr.UpdateStakePoolUserTickets(
						stakemgrNs, addrmgrNs, poolUser, poolTicket)
					if err != nil {
						log.Warnf("failed to update stake pool ticket for "+
							"stake pool user %s after revoking",
							poolUser.EncodeAddress())
					} else {
						log.Debugf("Updated missed stake pool ticket %v "+
							"for user %v into the stake store database ("+
							"revocation hash: %v)", txInHash, poolUser, txHash)
					}
				}
			}
		}
	}

	err := w.TxStore.InsertTx(txmgrNs, addrmgrNs, rec, block)
	if err != nil {
		return err
	}

	// Handle input scripts that contain P2PKs that we care about.
	for i, input := range rec.MsgTx.TxIn {
		if txscript.IsMultisigSigScript(input.SignatureScript) {
			rs, err :=
				txscript.MultisigRedeemScriptFromScriptSig(
					input.SignatureScript)
			if err != nil {
				return err
			}

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

			isRelevant := false
			for _, addr := range addrs {
				_, err := w.Manager.Address(addrmgrNs, addr)
				if err == nil {
					isRelevant = true
					err = w.Manager.MarkUsed(addrmgrNs, addr)
					if err != nil {
						return err
					}
					log.Debugf("Marked address %v used", addr)
				} else {
					// Missing addresses are skipped.  Other errors should
					// be propagated.
					if !waddrmgr.IsError(err, waddrmgr.ErrAddressNotFound) {
						return err
					}
				}
			}

			// Add the script to the script databases.
			// TODO Markused script address? cj
			if isRelevant {
				err = w.TxStore.InsertTxScript(txmgrNs, rs)
				if err != nil {
					return err
				}
				var blockToUse *waddrmgr.BlockStamp
				if block != nil {
					blockToUse = &waddrmgr.BlockStamp{
						Height: block.Height,
						Hash:   block.Hash,
					}
				}
				mscriptaddr, err := w.Manager.ImportScript(addrmgrNs, rs, blockToUse)
				if err != nil {
					switch {
					// Don't care if it's already there.
					case waddrmgr.IsError(err, waddrmgr.ErrDuplicateAddress):
						break
					case waddrmgr.IsError(err, waddrmgr.ErrLocked):
						log.Warnf("failed to attempt script importation "+
							"of incoming tx script %x because addrmgr "+
							"was locked", rs)
						break
					default:
						return err
					}
				} else {
					// This is the first time seeing this script address
					// belongs to us, so do a rescan and see if there are
					// any other outputs to this address.
					job := &RescanJob{
						Addrs:     []dcrutil.Address{mscriptaddr.Address()},
						OutPoints: nil,
						BlockStamp: waddrmgr.BlockStamp{
							Height: 0,
							Hash:   *w.chainParams.GenesisHash,
						},
					}

					// Submit rescan job and log when the import has completed.
					// Do not block on finishing the rescan.  The rescan success
					// or failure is logged elsewhere, and the channel is not
					// required to be read, so discard the return value.
					_ = w.SubmitRescan(job)
				}
			}

			// If we're spending a multisig outpoint we know about,
			// update the outpoint. Inefficient because you deserialize
			// the entire multisig output info. Consider a specific
			// exists function in wtxmgr. The error here is skipped
			// because the absence of an multisignature output for
			// some script can not always be considered an error. For
			// example, the wallet might be rescanning as called from
			// the above function and so does not have the output
			// included yet.
			mso, err := w.TxStore.GetMultisigOutput(txmgrNs, &input.PreviousOutPoint)
			if mso != nil && err == nil {
				w.TxStore.SpendMultisigOut(txmgrNs, &input.PreviousOutPoint,
					rec.Hash,
					uint32(i))
			}
		}
	}

	// Check every output to determine whether it is controlled by a wallet
	// key.  If so, mark the output as a credit.
	for i, output := range rec.MsgTx.TxOut {
		// Ignore unspendable outputs.
		if output.Value == 0 {
			continue
		}

		class, addrs, _, err := txscript.ExtractPkScriptAddrs(output.Version,
			output.PkScript, w.chainParams)
		if err != nil {
			// Non-standard outputs are skipped.
			continue
		}
		isStakeType := class == txscript.StakeSubmissionTy ||
			class == txscript.StakeSubChangeTy ||
			class == txscript.StakeGenTy ||
			class == txscript.StakeRevocationTy
		if isStakeType {
			class, err = txscript.GetStakeOutSubclass(output.PkScript)
			if err != nil {
				log.Errorf("Unknown stake output subclass parse error "+
					"encountered: %v", err)
				continue
			}
		}

		for _, addr := range addrs {
			ma, err := w.Manager.Address(addrmgrNs, addr)
			if err == nil {
				// TODO: Credits should be added with the
				// account they belong to, so wtxmgr is able to
				// track per-account balances.
				err = w.TxStore.AddCredit(txmgrNs, rec, block,
					uint32(i), ma.Internal(), ma.Account())
				if err != nil {
					return err
				}
				err = w.Manager.MarkUsed(addrmgrNs, addr)
				if err != nil {
					return err
				}
				log.Debugf("Marked address %v used", addr)
				continue
			}

			// Missing addresses are skipped.  Other errors should
			// be propagated.
			if !waddrmgr.IsError(err, waddrmgr.ErrAddressNotFound) {
				return err
			}
		}

		// Handle P2SH addresses that are multisignature scripts
		// with keys that we own.
		if class == txscript.ScriptHashTy {
			var expandedScript []byte
			for _, addr := range addrs {
				// Search both the script store in the tx store
				// and the address manager for the redeem script.
				var err error
				expandedScript, err =
					w.TxStore.GetTxScript(txmgrNs,
						addr.ScriptAddress())
				if err != nil {
					return err
				}

				if expandedScript == nil {
					scrAddr, err := w.Manager.Address(addrmgrNs, addr)
					if err == nil {
						sa, ok := scrAddr.(waddrmgr.ManagedScriptAddress)
						if !ok {
							log.Warnf("address %v is not a script"+
								" address (type %T)",
								scrAddr.Address().EncodeAddress(),
								scrAddr.Address())
							continue
						}
						retrievedScript, err := sa.Script()
						if err != nil {
							log.Errorf("failed to decode redeemscript for "+
								"address %v: %v", addr.EncodeAddress(),
								err.Error())
							continue
						}
						expandedScript = retrievedScript

					} else {
						// We can't find this redeem script anywhere.
						// Skip this output.
						log.Debugf("failed to find redeemscript for "+
							"address %v in address manager: %v",
							addr.EncodeAddress(), err.Error())
						continue
					}
				}
			}

			// Otherwise, extract the actual addresses and
			// see if any belong to us.
			expClass, multisigAddrs, _, err := txscript.ExtractPkScriptAddrs(
				txscript.DefaultScriptVersion,
				expandedScript,
				w.chainParams)
			if err != nil {
				return err
			}

			// Skip non-multisig scripts.
			if expClass != txscript.MultiSigTy {
				continue
			}

			for _, maddr := range multisigAddrs {
				_, err := w.Manager.Address(addrmgrNs, maddr)
				// An address we own; handle accordingly.
				if err == nil {
					errStore := w.TxStore.AddMultisigOut(
						txmgrNs, rec, block, uint32(i))
					if errStore != nil {
						// This will throw if there are multiple private keys
						// for this multisignature output owned by the wallet,
						// so it's routed to debug.
						log.Debugf("unable to add multisignature output: %v",
							errStore.Error())
					}
				}
			}
		}
	}

	// Send notification of mined or unmined transaction to any interested
	// clients.
	//
	// TODO: Avoid the extra db hits.
	if block == nil {
		details, err := w.TxStore.UniqueTxDetails(txmgrNs, &rec.Hash, nil)
		if err != nil {
			log.Errorf("Cannot query transaction details for notifiation: %v",
				err)
		} else {
			w.NtfnServer.notifyUnminedTransaction(dbtx, details)
		}
	} else {
		details, err := w.TxStore.UniqueTxDetails(txmgrNs, &rec.Hash, &block.Block)
		if err != nil {
			log.Errorf("Cannot query transaction details for notifiation: %v",
				err)
		} else {
			w.NtfnServer.notifyMinedTransaction(dbtx, details, block)
		}
	}

	return nil
}
Exemplo n.º 10
0
// generateVote creates a new SSGen given a header hash, height, sstx
// tx hash, and votebits.
func (s *StakeStore) generateVote(ns walletdb.ReadWriteBucket, waddrmgrNs walletdb.ReadBucket, blockHash *chainhash.Hash, height int64, sstxHash *chainhash.Hash, defaultVoteBits stake.VoteBits, allowHighFees bool) (*StakeNotification, error) {
	// 1. Fetch the SStx, then calculate all the values we'll need later for
	// the generation of the SSGen tx outputs.
	sstxRecord, err := s.getSStx(ns, sstxHash)
	if err != nil {
		return nil, err
	}
	sstx := sstxRecord.tx
	sstxMsgTx := sstx.MsgTx()

	// The legacy wallet didn't store anything about the voteBits to use.
	// In the case we're loading a legacy wallet and the voteBits are
	// unset, just use the default voteBits as set by the user.
	voteBits := defaultVoteBits
	if sstxRecord.voteBitsSet {
		voteBits.Bits = sstxRecord.voteBits
		voteBits.ExtendedBits = sstxRecord.voteBitsExt
	}

	// Store the sstx pubkeyhashes and amounts as found in the transaction
	// outputs.
	// TODO Get information on the allowable fee range for the vote
	// and check to make sure we don't overflow that.
	ssgenPayTypes, ssgenPkhs, sstxAmts, _, _, _ :=
		stake.TxSStxStakeOutputInfo(sstxMsgTx)

	// Get the current reward.
	initSudsidyCacheOnce.Do(func() {
		subsidyCache = blockchain.NewSubsidyCache(height, s.Params)
	})
	stakeVoteSubsidy := blockchain.CalcStakeVoteSubsidy(subsidyCache,
		height, s.Params)

	// Calculate the output values from this data.
	ssgenCalcAmts := stake.CalculateRewards(sstxAmts,
		sstxMsgTx.TxOut[0].Value,
		stakeVoteSubsidy)

	subsidyCache = blockchain.NewSubsidyCache(height, s.Params)
	// 2. Add all transaction inputs to a new transaction after performing
	// some validity checks. First, add the stake base, then the OP_SSTX
	// tagged output.
	msgTx := wire.NewMsgTx()

	// Stakebase.
	stakeBaseOutPoint := wire.NewOutPoint(&chainhash.Hash{},
		uint32(0xFFFFFFFF),
		wire.TxTreeRegular)
	txInStakeBase := wire.NewTxIn(stakeBaseOutPoint, []byte{})
	msgTx.AddTxIn(txInStakeBase)

	// Add the subsidy amount into the input.
	msgTx.TxIn[0].ValueIn = stakeVoteSubsidy

	// SStx tagged output as an OutPoint.
	prevOut := wire.NewOutPoint(sstxHash,
		0, // Index 0
		1) // Tree stake
	txIn := wire.NewTxIn(prevOut, []byte{})
	msgTx.AddTxIn(txIn)

	// 3. Add the OP_RETURN null data pushes of the block header hash,
	// the block height, and votebits, then add all the OP_SSGEN tagged
	// outputs.
	//
	// Block reference output.
	blockRefScript, err := txscript.GenerateSSGenBlockRef(*blockHash,
		uint32(height))
	if err != nil {
		return nil, err
	}
	blockRefOut := wire.NewTxOut(0, blockRefScript)
	msgTx.AddTxOut(blockRefOut)

	// Votebits output.
	blockVBScript, err := generateVoteScript(voteBits)
	if err != nil {
		return nil, err
	}
	blockVBOut := wire.NewTxOut(0, blockVBScript)
	msgTx.AddTxOut(blockVBOut)

	// Add all the SSGen-tagged transaction outputs to the transaction after
	// performing some validity checks.
	for i, ssgenPkh := range ssgenPkhs {
		// Create a new script which pays to the provided address specified in
		// the original ticket tx.
		var ssgenOutScript []byte
		switch ssgenPayTypes[i] {
		case false: // P2PKH
			ssgenOutScript, err = txscript.PayToSSGenPKHDirect(ssgenPkh)
			if err != nil {
				return nil, err
			}
		case true: // P2SH
			ssgenOutScript, err = txscript.PayToSSGenSHDirect(ssgenPkh)
			if err != nil {
				return nil, err
			}
		}

		// Add the txout to our SSGen tx.
		txOut := wire.NewTxOut(ssgenCalcAmts[i], ssgenOutScript)

		msgTx.AddTxOut(txOut)
	}

	// Check to make sure our SSGen was created correctly.
	_, err = stake.IsSSGen(msgTx)
	if err != nil {
		return nil, err
	}

	// Sign the transaction.
	err = s.SignVRTransaction(waddrmgrNs, msgTx, sstx, true)
	if err != nil {
		return nil, err
	}

	// Store the information about the SSGen.
	hash := msgTx.TxSha()
	err = s.insertSSGen(ns,
		blockHash,
		height,
		&hash,
		voteBits.Bits,
		sstx.Sha())
	if err != nil {
		return nil, err
	}

	// Send the transaction.
	ssgenSha, err := s.chainSvr.SendRawTransaction(msgTx, allowHighFees)
	if err != nil {
		return nil, err
	}

	log.Debugf("Generated SSGen %v, voting on block %v at height %v. "+
		"The ticket used to generate the SSGen was %v.",
		ssgenSha, blockHash, height, sstxHash)

	// Generate a notification to return.
	ntfn := &StakeNotification{
		TxType:    int8(stake.TxTypeSSGen),
		TxHash:    *ssgenSha,
		BlockHash: *blockHash,
		Height:    int32(height),
		Amount:    0,
		SStxIn:    *sstx.Sha(),
		VoteBits:  voteBits.Bits,
	}

	return ntfn, nil
}
Exemplo n.º 11
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)
		}
	}
}
Exemplo n.º 12
0
// connectTickets updates the passed map by removing removing any tickets
// from the ticket pool that have been considered spent or missed in this block
// according to the block header. Then, it connects all the newly mature tickets
// to the passed map.
func (b *BlockChain) connectTickets(tixStore TicketStore,
	node *blockNode,
	block *dcrutil.Block) error {
	if tixStore == nil {
		return fmt.Errorf("nil ticket store!")
	}

	// Nothing to do if tickets haven't yet possibly matured.
	height := node.height
	if height < b.chainParams.StakeEnabledHeight {
		return nil
	}

	parentBlock, err := b.GetBlockFromHash(node.parentHash)
	if err != nil {
		return err
	}

	revocations := node.header.Revocations

	tM := int64(b.chainParams.TicketMaturity)

	// Skip a number of validation steps before we requiring chain
	// voting.
	if node.height >= b.chainParams.StakeValidationHeight {
		regularTxTreeValid := dcrutil.IsFlagSet16(node.header.VoteBits,
			dcrutil.BlockValid)
		thisNodeStakeViewpoint := ViewpointPrevInvalidStake
		if regularTxTreeValid {
			thisNodeStakeViewpoint = ViewpointPrevValidStake
		}

		// We need the missed tickets bucket from the original perspective of
		// the node.
		missedTickets, err := b.GenerateMissedTickets(tixStore)
		if err != nil {
			return err
		}

		// TxStore at blockchain HEAD + TxTreeRegular of prevBlock (if
		// validated) for this node.
		txInputStoreStake, err := b.fetchInputTransactions(node, block,
			thisNodeStakeViewpoint)
		if err != nil {
			errStr := fmt.Sprintf("fetchInputTransactions failed for incoming "+
				"node %v; error given: %v", node.hash, err)
			return errors.New(errStr)
		}

		// PART 1: Spend/miss winner tickets

		// Iterate through all the SSGen (vote) tx in the block and add them to
		// a map of tickets that were actually used.
		spentTicketsFromBlock := make(map[chainhash.Hash]bool)
		numberOfSSgen := 0
		for _, staketx := range block.STransactions() {
			if is, _ := stake.IsSSGen(staketx); is {
				msgTx := staketx.MsgTx()
				sstxIn := msgTx.TxIn[1] // sstx input
				sstxHash := sstxIn.PreviousOutPoint.Hash

				originTx, exists := txInputStoreStake[sstxHash]
				if !exists {
					str := fmt.Sprintf("unable to find input transaction "+
						"%v for transaction %v", sstxHash, staketx.Sha())
					return ruleError(ErrMissingTx, str)
				}

				sstxHeight := originTx.BlockHeight

				// Check maturity of ticket; we can only spend the ticket after it
				// hits maturity at height + tM + 1.
				if (height - sstxHeight) < (tM + 1) {
					blockSha := block.Sha()
					errStr := fmt.Sprintf("Error: A ticket spend as an SSGen in "+
						"block height %v was immature! Block sha %v",
						height,
						blockSha)
					return errors.New(errStr)
				}

				// Fill out the ticket data.
				spentTicketsFromBlock[sstxHash] = true
				numberOfSSgen++
			}
		}

		// Obtain the TicketsPerBlock many tickets that were selected this round,
		// then check these against the tickets that were actually used to make
		// sure that any SSGen actually match the selected tickets. Commit the
		// spent or missed tickets to the ticket store after.
		spentAndMissedTickets := make(TicketStore)
		tixSpent := 0
		tixMissed := 0

		// Sort the entire list of tickets lexicographically by sorting
		// each bucket and then appending it to the list. Start by generating
		// a prefix matched map of tickets to speed up the lookup.
		tpdBucketMap := make(map[uint8][]*TicketPatchData)
		for _, tpd := range tixStore {
			// Bucket does not exist.
			if _, ok := tpdBucketMap[tpd.td.Prefix]; !ok {
				tpdBucketMap[tpd.td.Prefix] = make([]*TicketPatchData, 1)
				tpdBucketMap[tpd.td.Prefix][0] = tpd
			} else {
				// Bucket exists.
				data := tpdBucketMap[tpd.td.Prefix]
				tpdBucketMap[tpd.td.Prefix] = append(data, tpd)
			}
		}
		totalTickets := 0
		sortedSlice := make([]*stake.TicketData, 0)
		for i := 0; i < stake.BucketsSize; i++ {
			ltb, err := b.GenerateLiveTicketBucket(tixStore, tpdBucketMap,
				uint8(i))
			if err != nil {
				h := node.hash
				str := fmt.Sprintf("Failed to generate live ticket bucket "+
					"%v for node %v, height %v! Error: %v",
					i,
					h,
					node.height,
					err.Error())
				return fmt.Errorf(str)
			}
			mapLen := len(ltb)

			tempTdSlice := stake.NewTicketDataSlice(mapLen)
			itr := 0 // Iterator
			for _, td := range ltb {
				tempTdSlice[itr] = td
				itr++
				totalTickets++
			}
			sort.Sort(tempTdSlice)
			sortedSlice = append(sortedSlice, tempTdSlice...)
		}

		// Use the parent block's header to seed a PRNG that picks the
		// lottery winners.
		ticketsPerBlock := int(b.chainParams.TicketsPerBlock)
		pbhB, err := parentBlock.MsgBlock().Header.Bytes()
		if err != nil {
			return err
		}
		prng := stake.NewHash256PRNG(pbhB)
		ts, err := stake.FindTicketIdxs(int64(totalTickets), ticketsPerBlock, prng)
		if err != nil {
			return err
		}

		ticketsToSpendOrMiss := make([]*stake.TicketData, ticketsPerBlock,
			ticketsPerBlock)
		for i, idx := range ts {
			ticketsToSpendOrMiss[i] = sortedSlice[idx]
		}

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

			if wasSpent {
				tpd := NewTicketPatchData(ticket, TiSpent, nil)
				spentAndMissedTickets[ticket.SStxHash] = tpd
				tixSpent++
			} else { // Ticket missed being spent and --> false or nil
				tpd := NewTicketPatchData(ticket, TiMissed, nil)
				spentAndMissedTickets[ticket.SStxHash] = tpd
				tixMissed++
			}
		}

		// This error is thrown if for some reason there exists an SSGen in
		// the block that doesn't spend a ticket from the eligible list of
		// tickets, thus making it invalid.
		if tixSpent != numberOfSSgen {
			errStr := fmt.Sprintf("an invalid number %v "+
				"tickets was spent, but %v many tickets should "+
				"have been spent!", tixSpent, numberOfSSgen)
			return errors.New(errStr)
		}

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

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

		// Calculate all the tickets expiring this block and mark them as missed.
		tpdBucketMap = make(map[uint8][]*TicketPatchData)
		for _, tpd := range tixStore {
			// Bucket does not exist.
			if _, ok := tpdBucketMap[tpd.td.Prefix]; !ok {
				tpdBucketMap[tpd.td.Prefix] = make([]*TicketPatchData, 1)
				tpdBucketMap[tpd.td.Prefix][0] = tpd
			} else {
				// Bucket exists.
				data := tpdBucketMap[tpd.td.Prefix]
				tpdBucketMap[tpd.td.Prefix] = append(data, tpd)
			}
		}
		toExpireHeight := node.height - int64(b.chainParams.TicketExpiry)
		if !(toExpireHeight < int64(b.chainParams.StakeEnabledHeight)) {
			for i := 0; i < stake.BucketsSize; i++ {
				// Generate the live ticket bucket.
				ltb, err := b.GenerateLiveTicketBucket(tixStore,
					tpdBucketMap, uint8(i))
				if err != nil {
					return err
				}

				for _, ticket := range ltb {
					if ticket.BlockHeight == toExpireHeight {
						tpd := NewTicketPatchData(ticket, TiMissed, nil)
						spentAndMissedTickets[ticket.SStxHash] = tpd
					}
				}
			}
		}

		// Merge the ticket store patch containing the spent and missed tickets
		// with the ticket store.
		for hash, tpd := range spentAndMissedTickets {
			tixStore[hash] = tpd
		}

		// At this point our tixStore now contains all the spent and missed tx
		// as per this block.

		// PART 2: Remove tickets that were missed and are now revoked.

		// Iterate through all the SSGen (vote) tx in the block and add them to
		// a map of tickets that were actually used.
		revocationsFromBlock := make(map[chainhash.Hash]struct{})
		numberOfSSRtx := 0
		for _, staketx := range block.STransactions() {
			if is, _ := stake.IsSSRtx(staketx); is {
				msgTx := staketx.MsgTx()
				sstxIn := msgTx.TxIn[0] // sstx input
				sstxHash := sstxIn.PreviousOutPoint.Hash

				// Fill out the ticket data.
				revocationsFromBlock[sstxHash] = struct{}{}
				numberOfSSRtx++
			}
		}

		if numberOfSSRtx != int(revocations) {
			errStr := fmt.Sprintf("an invalid revocations %v was calculated "+
				"the block header indicates %v instead", numberOfSSRtx,
				revocations)
			return errors.New(errStr)
		}

		// Lookup the missed ticket. If we find it in the patch data,
		// modify the patch data so that it doesn't exist.
		// Otherwise, just modify load the missed ticket data from
		// the ticket db and create patch data based on that.
		for hash, _ := range revocationsFromBlock {
			ticketWasMissed := false
			if td, is := missedTickets[hash]; is {
				maturedHeight := td.BlockHeight

				// Check maturity of ticket; we can only spend the ticket after it
				// hits maturity at height + tM + 2.
				if height < maturedHeight+2 {
					blockSha := block.Sha()
					errStr := fmt.Sprintf("Error: A ticket spend as an "+
						"SSRtx in block height %v was immature! Block sha %v",
						height,
						blockSha)
					return errors.New(errStr)
				}

				ticketWasMissed = true
			}

			if !ticketWasMissed {
				errStr := fmt.Sprintf("SSRtx spent missed sstx %v, "+
					"but that missed sstx could not be found!",
					hash)
				return errors.New(errStr)
			}
		}
	}

	// PART 3: Add newly maturing tickets
	// This is the only chunk we need to do for blocks appearing before
	// stake validation height.

	// Calculate block number for where new tickets are maturing from and retrieve
	// this block from db.

	// Get the block that is maturing.
	matureNode, err := b.getNodeAtHeightFromTopNode(node, tM)
	if err != nil {
		return err
	}

	matureBlock, errBlock := b.getBlockFromHash(matureNode.hash)
	if errBlock != nil {
		return errBlock
	}

	// Maturing tickets are from the maturingBlock; fill out the ticket patch data
	// and then push them to the tixStore.
	for _, stx := range matureBlock.STransactions() {
		if is, _ := stake.IsSStx(stx); is {
			// Calculate the prefix for pre-sort.
			sstxHash := *stx.Sha()
			prefix := uint8(sstxHash[0])

			// Fill out the ticket data.
			td := stake.NewTicketData(sstxHash,
				prefix,
				chainhash.Hash{},
				height,
				false, // not missed
				false) // not expired

			tpd := NewTicketPatchData(td,
				TiAvailable,
				nil)
			tixStore[*stx.Sha()] = tpd
		}
	}

	return nil
}
Exemplo n.º 13
0
Arquivo: main.go Projeto: decred/dcrd
// traceDevPremineOuts returns a list of outpoints that are part of the dev
// premine coins and are ancestors of the inputs to the passed transaction hash.
func traceDevPremineOuts(client *dcrrpcclient.Client, txHash *chainhash.Hash) ([]wire.OutPoint, error) {
	// Trace the lineage of all inputs to the provided transaction back to
	// the coinbase outputs that generated them and add those outpoints to
	// a list.  Also, keep track of all of the processed transactions in
	// order to avoid processing duplicates.
	knownCoinbases := make(map[chainhash.Hash]struct{})
	processedHashes := make(map[chainhash.Hash]struct{})
	coinbaseOuts := make([]wire.OutPoint, 0, 10)
	processOuts := []wire.OutPoint{{Hash: *txHash}}
	for len(processOuts) > 0 {
		// Grab the first outpoint to process and skip it if it has
		// already been traced.
		outpoint := processOuts[0]
		processOuts = processOuts[1:]
		if _, exists := processedHashes[outpoint.Hash]; exists {
			if _, exists := knownCoinbases[outpoint.Hash]; exists {
				coinbaseOuts = append(coinbaseOuts, outpoint)
			}
			continue
		}
		processedHashes[outpoint.Hash] = struct{}{}

		// Request the transaction for the outpoint from the server.
		tx, err := client.GetRawTransaction(&outpoint.Hash)
		if err != nil {
			return nil, fmt.Errorf("failed to get transaction %v: %v",
				&outpoint.Hash, err)
		}

		// Add the outpoint to the coinbase outputs list when it is part
		// of a coinbase transaction.  Also, keep track of the fact the
		// transaction is a coinbase to use when avoiding duplicate
		// checks.
		if blockchain.IsCoinBase(tx) {
			knownCoinbases[outpoint.Hash] = struct{}{}
			coinbaseOuts = append(coinbaseOuts, outpoint)
			continue
		}

		// Add the inputs to the transaction to the list of transactions
		// to load and continue tracing.
		//
		// However, skip the first input to stake generation txns since
		// they are creating new coins.  The remaining inputs to a
		// stake generation transaction still need to be traced since
		// they represent the coins that purchased the ticket.
		txIns := tx.MsgTx().TxIn
		isSSGen, _ := stake.IsSSGen(tx.MsgTx())
		if isSSGen {
			txIns = txIns[1:]
		}
		for _, txIn := range txIns {
			processOuts = append(processOuts, txIn.PreviousOutPoint)
		}
	}

	// Add any of the outputs that are dev premine outputs to a list.
	var devPremineOuts []wire.OutPoint
	for _, coinbaseOut := range coinbaseOuts {
		if isDevPremineOut(coinbaseOut) {
			devPremineOuts = append(devPremineOuts, coinbaseOut)
		}
	}

	return devPremineOuts, nil
}
Exemplo n.º 14
0
// maybeAcceptBlock potentially accepts a block into the memory block chain.
// It performs several validation checks which depend on its position within
// the block chain before adding it.  The block is expected to have already gone
// through ProcessBlock before calling this function with it.
//
// The flags modify the behavior of this function as follows:
//  - BFDryRun: The memory chain index will not be pruned and no accept
//    notification will be sent since the block is not being accepted.
func (b *BlockChain) maybeAcceptBlock(block *dcrutil.Block,
	flags BehaviorFlags) (bool, error) {
	dryRun := flags&BFDryRun == BFDryRun

	// Get a block node for the block previous to this one.  Will be nil
	// if this is the genesis block.
	prevNode, err := b.getPrevNodeFromBlock(block)
	if err != nil {
		log.Debugf("getPrevNodeFromBlock: %v", err)
		return false, err
	}

	// The height of this block is one more than the referenced previous
	// block.
	blockHeight := int64(0)
	if prevNode != nil {
		blockHeight = prevNode.height + 1
	}
	block.SetHeight(blockHeight)

	// The block must pass all of the validation rules which depend on the
	// position of the block within the block chain.
	err = b.checkBlockContext(block, prevNode, flags)
	if err != nil {
		return false, err
	}

	// Prune block nodes which are no longer needed before creating
	// a new node.
	if !dryRun {
		err = b.pruneBlockNodes()
		if err != nil {
			return false, err
		}
	}

	// Create a new block node for the block and add it to the in-memory
	// block chain (could be either a side chain or the main chain).
	blockHeader := &block.MsgBlock().Header
	var voteBitsStake []uint16
	for _, stx := range block.STransactions() {
		if is, _ := stake.IsSSGen(stx); is {
			vb := stake.GetSSGenVoteBits(stx)
			voteBitsStake = append(voteBitsStake, vb)
		}
	}

	newNode := newBlockNode(blockHeader, block.Sha(), blockHeight, voteBitsStake)
	if prevNode != nil {
		newNode.parent = prevNode
		newNode.height = blockHeight
		newNode.workSum.Add(prevNode.workSum, newNode.workSum)
	}

	// Connect the passed block to the chain while respecting proper chain
	// selection according to the chain with the most proof of work.  This
	// also handles validation of the transaction scripts.
	var onMainChain bool
	onMainChain, err = b.connectBestChain(newNode, block, flags)
	if err != nil {
		return false, err
	}

	// Notify the caller that the new block was accepted into the block
	// chain.  The caller would typically want to react by relaying the
	// inventory to other peers.
	if !dryRun {
		b.sendNotification(NTBlockAccepted,
			&BlockAcceptedNtfnsData{onMainChain, block})
	}

	return onMainChain, nil
}
Exemplo n.º 15
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")
}
Exemplo n.º 16
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
}
Exemplo n.º 17
0
func (w *Wallet) addRelevantTx(rec *wtxmgr.TxRecord,
	block *wtxmgr.BlockMeta) error {
	// TODO: The transaction store and address manager need to be updated
	// together, but each operate under different namespaces and are changed
	// under new transactions.  This is not error safe as we lose
	// transaction semantics.
	//
	// I'm unsure of the best way to solve this.  Some possible solutions
	// and drawbacks:
	//
	//   1. Open write transactions here and pass the handle to every
	//      waddrmr and wtxmgr method.  This complicates the caller code
	//      everywhere, however.
	//
	//   2. Move the wtxmgr namespace into the waddrmgr namespace, likely
	//      under its own bucket.  This entire function can then be moved
	//      into the waddrmgr package, which updates the nested wtxmgr.
	//      This removes some of separation between the components.
	//
	//   3. Use multiple wtxmgrs, one for each account, nested in the
	//      waddrmgr namespace.  This still provides some sort of logical
	//      separation (transaction handling remains in another package, and
	//      is simply used by waddrmgr), but may result in duplicate
	//      transactions being saved if they are relevant to multiple
	//      accounts.
	//
	//   4. Store wtxmgr-related details under the waddrmgr namespace, but
	//      solve the drawback of #3 by splitting wtxmgr to save entire
	//      transaction records globally for all accounts, with
	//      credit/debit/balance tracking per account.  Each account would
	//      also save the relevant transaction hashes and block incidence so
	//      the full transaction can be loaded from the waddrmgr
	//      transactions bucket.  This currently seems like the best
	//      solution.

	// At the moment all notified transactions are assumed to actually be
	// relevant.  This assumption will not hold true when SPV support is
	// added, but until then, simply insert the transaction because there
	// should either be one or more relevant inputs or outputs.
	//
	// TODO This function is pretty bad corruption wise, it's very easy
	// to corrupt the wallet if you ctrl+c while in this function. This
	// needs desperate refactoring.

	tx := dcrutil.NewTx(&rec.MsgTx)

	// Handle incoming SStx; store them in the stake manager if we own
	// the OP_SSTX tagged out.
	if is, _ := stake.IsSStx(tx); is {
		// Errors don't matter here.  If addrs is nil, the range below
		// does nothing.
		txOut := tx.MsgTx().TxOut[0]

		_, addrs, _, _ := txscript.ExtractPkScriptAddrs(txOut.Version,
			txOut.PkScript, w.chainParams)
		insert := false
		for _, addr := range addrs {
			_, err := w.Manager.Address(addr)
			if err == nil {
				insert = true
				break
			}
		}

		if insert {
			err := w.StakeMgr.InsertSStx(tx)
			if err != nil {
				log.Errorf("Failed to insert SStx %v"+
					"into the stake store.", tx.Sha())
			}
		}
	}

	// Handle incoming SSGen; store them if we own
	// the ticket used to purchase them.
	if is, _ := stake.IsSSGen(tx); is {
		if block != nil {
			txInHash := tx.MsgTx().TxIn[1].PreviousOutPoint.Hash
			if w.StakeMgr.CheckHashInStore(&txInHash) {
				w.StakeMgr.InsertSSGen(&block.Hash,
					int64(block.Height),
					tx.Sha(),
					w.VoteBits,
					&txInHash)
			}
		} else {
			// If there's no associated block, it's potentially a
			// doublespent SSGen. Just ignore it and wait for it
			// to later get into a block.
			return nil
		}
	}

	// Handle incoming SSRtx; store them if we own
	// the ticket used to purchase them.
	if is, _ := stake.IsSSRtx(tx); is {
		if block != nil {
			txInHash := tx.MsgTx().TxIn[0].PreviousOutPoint.Hash

			if w.StakeMgr.CheckHashInStore(&txInHash) {
				w.StakeMgr.InsertSSRtx(&block.Hash,
					int64(block.Height),
					tx.Sha(),
					&txInHash)
			}
		}
	}

	err := w.TxStore.InsertTx(rec, block)
	if err != nil {
		return err
	}

	// Handle input scripts that contain P2PKs that we care about.
	for i, input := range rec.MsgTx.TxIn {
		if txscript.IsMultisigSigScript(input.SignatureScript) {
			rs, err :=
				txscript.MultisigRedeemScriptFromScriptSig(
					input.SignatureScript)
			if err != nil {
				return err
			}

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

			isRelevant := false
			for _, addr := range addrs {
				_, err := w.Manager.Address(addr)
				if err == nil {
					isRelevant = true
					err = w.Manager.MarkUsed(addr)
					if err != nil {
						return err
					}
					log.Debugf("Marked address %v used", addr)
				} else {
					// Missing addresses are skipped.  Other errors should
					// be propagated.
					if !waddrmgr.IsError(err, waddrmgr.ErrAddressNotFound) {
						return err
					}
				}
			}

			// Add the script to the script databases.
			// TODO Markused script address? cj
			if isRelevant {
				err = w.TxStore.InsertTxScript(rs)
				if err != nil {
					return err
				}
				var blockToUse *waddrmgr.BlockStamp
				if block != nil {
					blockToUse = &waddrmgr.BlockStamp{block.Height, block.Hash}
				}
				mscriptaddr, err := w.Manager.ImportScript(rs, blockToUse)
				if err != nil {
					switch {
					// Don't care if it's already there.
					case waddrmgr.IsError(err, waddrmgr.ErrDuplicateAddress):
						break
					case waddrmgr.IsError(err, waddrmgr.ErrLocked):
						log.Debugf("failed to attempt script importation " +
							"of incoming tx because addrmgr was locked")
						break
					default:
						return err
					}
				} else {
					// This is the first time seeing this script address
					// belongs to us, so do a rescan and see if there are
					// any other outputs to this address.
					job := &RescanJob{
						Addrs:     []dcrutil.Address{mscriptaddr.Address()},
						OutPoints: nil,
						BlockStamp: waddrmgr.BlockStamp{
							0,
							*w.chainParams.GenesisHash,
						},
					}

					// Submit rescan job and log when the import has completed.
					// Do not block on finishing the rescan.  The rescan success
					// or failure is logged elsewhere, and the channel is not
					// required to be read, so discard the return value.
					_ = w.SubmitRescan(job)
				}
			}

			// If we're spending a multisig outpoint we
			// know about, update the outpoint.
			// Inefficient because you deserialize the
			// entire multisig output info, consider
			// a specific exists function in wtxmgr. cj
			mso, err := w.TxStore.GetMultisigOutput(&input.PreviousOutPoint)
			if err != nil {
				return err
			}
			if mso != nil {
				w.TxStore.SpendMultisigOut(&input.PreviousOutPoint,
					rec.Hash,
					uint32(i))
			}
		}
	}

	// Check every output to determine whether it is controlled by a wallet
	// key.  If so, mark the output as a credit.
	for i, output := range rec.MsgTx.TxOut {
		class, addrs, _, err := txscript.ExtractPkScriptAddrs(output.Version,
			output.PkScript, w.chainParams)
		if err != nil {
			// Non-standard outputs are skipped.
			continue
		}
		isStakeType := class == txscript.StakeSubmissionTy ||
			class == txscript.StakeSubChangeTy ||
			class == txscript.StakeGenTy ||
			class == txscript.StakeRevocationTy
		if isStakeType {
			class, err = txscript.GetStakeOutSubclass(output.PkScript)
			if err != nil {
				log.Errorf("Unknown stake output subclass encountered")
				continue
			}
		}
		switch {
		case class == txscript.PubKeyHashTy:
			for _, addr := range addrs {
				ma, err := w.Manager.Address(addr)
				if err == nil {
					// TODO: Credits should be added with the
					// account they belong to, so wtxmgr is able to
					// track per-account balances.
					err = w.TxStore.AddCredit(rec, block, uint32(i),
						ma.Internal())
					if err != nil {
						return err
					}
					err = w.Manager.MarkUsed(addr)
					if err != nil {
						return err
					}
					log.Debugf("Marked address %v used", addr)
					continue
				}

				// Missing addresses are skipped.  Other errors should
				// be propagated.
				if !waddrmgr.IsError(err, waddrmgr.ErrAddressNotFound) {
					return err
				}
			}
		// Handle P2SH addresses that are multisignature scripts
		// with keys that we own.
		case class == txscript.ScriptHashTy:
			var expandedScript []byte
			for _, addr := range addrs {
				var err error
				expandedScript, err =
					w.TxStore.GetTxScript(addr.ScriptAddress())
				if err != nil {
					return err
				}

				// TODO make this work, the type conversion is broken cj
				//scrAddr, err := w.Manager.Address(addr)
				//if err == nil {
				//	addrTyped := scrAddr.(*waddrmgr.ManagedScriptAddress)
				//	retrievedScript, err := addrTyped.Script()
				//	if err == nil {
				//		expandedScript = retrievedScript
				//	}
				//}
			}

			// We don't have the script for this hash, skip.
			if expandedScript == nil {
				continue
			}

			// Otherwise, extract the actual addresses and
			// see if any belong to us.
			expClass, multisigAddrs, _, err := txscript.ExtractPkScriptAddrs(
				txscript.DefaultScriptVersion,
				expandedScript,
				w.chainParams)
			if err != nil {
				return err
			}

			// Skip non-multisig scripts.
			if expClass != txscript.MultiSigTy {
				continue
			}

			for _, maddr := range multisigAddrs {
				_, err := w.Manager.Address(maddr)
				// An address we own; handle accordingly.
				if err == nil {
					errStore := w.TxStore.AddMultisigOut(rec, block, uint32(i))
					if errStore != nil {
						// This will throw if there are multiple private keys
						// for this multisignature output owned by the wallet,
						// so it's routed to debug.
						log.Debugf("unable to add multisignature output: %v",
							errStore.Error())
					}
				}
			}
		}
	}

	// TODO: Notify connected clients of the added transaction.

	bs, err := w.chainSvr.BlockStamp()
	if err == nil {
		w.notifyBalances(bs.Height, wtxmgr.BFBalanceSpendable)
	}

	return nil
}