Пример #1
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
}
Пример #2
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
}
Пример #3
0
// addUnconfirmedTx adds all addresses related to the transaction to the
// unconfirmed (memory-only) exists address index.
func (idx *ExistsAddrIndex) addUnconfirmedTx(tx *wire.MsgTx) {
	isSStx, _ := stake.IsSStx(tx)
	for _, txIn := range tx.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
				}

				if _, exists := idx.mpExistsAddr[k]; !exists {
					idx.mpExistsAddr[k] = struct{}{}
				}
			}
		}
	}

	for _, txOut := range tx.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
			}

			if _, exists := idx.mpExistsAddr[k]; !exists {
				idx.mpExistsAddr[k] = struct{}{}
			}
		}
	}
}
Пример #4
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
}