Пример #1
0
// TicketsWithAddress returns a slice of ticket hashes that are currently live
// corresponding to the given address.
//
// This function is safe for concurrent access.
func (b *BlockChain) TicketsWithAddress(address dcrutil.Address) ([]chainhash.Hash, error) {
	b.chainLock.RLock()
	sn := b.bestNode.stakeNode
	b.chainLock.RUnlock()

	tickets := sn.LiveTickets()

	var ticketsWithAddr []chainhash.Hash
	err := b.db.View(func(dbTx database.Tx) error {
		var err error
		for _, hash := range tickets {
			utxo, err := dbFetchUtxoEntry(dbTx, &hash)
			if err != nil {
				return err
			}

			_, addrs, _, err :=
				txscript.ExtractPkScriptAddrs(txscript.DefaultScriptVersion,
					utxo.PkScriptByIndex(0), b.chainParams)
			if addrs[0].EncodeAddress() == address.EncodeAddress() {
				ticketsWithAddr = append(ticketsWithAddr, hash)
			}
		}
		return err
	})
	if err != nil {
		return nil, err
	}

	return ticketsWithAddr, nil
}
Пример #2
0
// GetLiveTicketsInBucketData creates a map indicating the ticket hash and the
// owner's address for each bucket. Used for an RPC call.
func (tmdb *TicketDB) GetLiveTicketsInBucketData(
	bucket uint8) (map[chainhash.Hash]dcrutil.Address, error) {
	tmdb.mtx.Lock()
	defer tmdb.mtx.Unlock()

	ltbd := make(map[chainhash.Hash]dcrutil.Address)
	tickets := tmdb.maps.ticketMap[bucket]
	for _, ticket := range tickets {
		// Load the ticket from the database and find the address that it's
		// going to.
		txReply, err := tmdb.database.FetchTxBySha(&ticket.SStxHash)
		if err != nil {
			return nil, err
		}

		_, addr, _, err :=
			txscript.ExtractPkScriptAddrs(txReply[0].Tx.TxOut[0].Version,
				txReply[0].Tx.TxOut[0].PkScript, tmdb.chainParams)
		if err != nil {
			return nil, err
		}
		ltbd[ticket.SStxHash] = addr[0]
	}

	return ltbd, nil
}
Пример #3
0
// This example demonstrates extracting information from a standard public key
// script.
func ExampleExtractPkScriptAddrs() {
	// Start with a standard pay-to-pubkey-hash script.
	scriptHex := "76a914128004ff2fcaf13b2b91eb654b1dc2b674f7ec6188ac"
	script, err := hex.DecodeString(scriptHex)
	if err != nil {
		fmt.Println(err)
		return
	}

	// Extract and print details from the script.
	scriptClass, addresses, reqSigs, err := txscript.ExtractPkScriptAddrs(
		txscript.DefaultScriptVersion, script, &chaincfg.MainNetParams)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("Script Class:", scriptClass)
	fmt.Println("Addresses:", addresses)
	fmt.Println("Required Signatures:", reqSigs)

	// Output:
	// Script Class: pubkeyhash
	// Addresses: [DsSej1qR3Fyc8kV176DCh9n9cY9nqf9Quxk]
	// Required Signatures: 1
}
Пример #4
0
func lookupInputAccount(dbtx walletdb.ReadTx, w *Wallet, details *wtxmgr.TxDetails, deb wtxmgr.DebitRecord) uint32 {
	addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
	txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)

	// TODO: Debits should record which account(s?) they
	// debit from so this doesn't need to be looked up.
	prevOP := &details.MsgTx.TxIn[deb.Index].PreviousOutPoint
	prev, err := w.TxStore.TxDetails(txmgrNs, &prevOP.Hash)
	if err != nil {
		log.Errorf("Cannot query previous transaction details for %v: %v", prevOP.Hash, err)
		return 0
	}
	if prev == nil {
		log.Errorf("Missing previous transaction %v", prevOP.Hash)
		return 0
	}
	prevOut := prev.MsgTx.TxOut[prevOP.Index]
	_, addrs, _, err := txscript.ExtractPkScriptAddrs(prevOut.Version, prevOut.PkScript, w.chainParams)
	var inputAcct uint32
	if err == nil && len(addrs) > 0 {
		inputAcct, err = w.Manager.AddrAccount(addrmgrNs, addrs[0])
	}
	if err != nil {
		log.Errorf("Cannot fetch account for previous output %v: %v", prevOP, err)
		inputAcct = 0
	}
	return inputAcct
}
Пример #5
0
// GetLiveTicketsForAddress gets all currently active tickets for a given
// address.
func (tmdb *TicketDB) GetLiveTicketsForAddress(
	address dcrutil.Address) ([]chainhash.Hash, error) {
	tmdb.mtx.Lock()
	defer tmdb.mtx.Unlock()

	var ltfa []chainhash.Hash
	for i := 0; i < BucketsSize; i++ {
		for _, ticket := range tmdb.maps.ticketMap[i] {
			// Load the ticket from the database and find the address that it's
			// going to.
			txReply, err := tmdb.database.FetchTxBySha(&ticket.SStxHash)
			if err != nil {
				return nil, err
			}

			_, addr, _, err :=
				txscript.ExtractPkScriptAddrs(txReply[0].Tx.TxOut[0].Version,
					txReply[0].Tx.TxOut[0].PkScript, tmdb.chainParams)
			if err != nil {
				return nil, err
			}

			// Compare the HASH160 result and see if it's equal.
			if bytes.Equal(addr[0].ScriptAddress(), address.ScriptAddress()) {
				ltfa = append(ltfa, ticket.SStxHash)
			}
		}
	}

	return ltfa, nil
}
Пример #6
0
// signMultiSigUTXO signs the P2SH UTXO with the given index by constructing a
// script containing all given signatures plus the redeem (multi-sig) script. The
// redeem script is obtained by looking up the address of the given P2SH pkScript
// on the address manager.
// The order of the signatures must match that of the public keys in the multi-sig
// script as OP_CHECKMULTISIG expects that.
// This function must be called with the manager unlocked.
func signMultiSigUTXO(mgr *waddrmgr.Manager, tx *wire.MsgTx, idx int, pkScript []byte, sigs []RawSig) error {
	class, addresses, _, err := txscript.ExtractPkScriptAddrs(txscript.DefaultScriptVersion, pkScript, mgr.ChainParams())
	if err != nil {
		return newError(ErrTxSigning, "unparseable pkScript", err)
	}
	if class != txscript.ScriptHashTy {
		return newError(ErrTxSigning, fmt.Sprintf("pkScript is not P2SH: %s", class), nil)
	}
	redeemScript, err := getRedeemScript(mgr, addresses[0].(*dcrutil.AddressScriptHash))
	if err != nil {
		return newError(ErrTxSigning, "unable to retrieve redeem script", err)
	}

	class, _, nRequired, err := txscript.ExtractPkScriptAddrs(txscript.DefaultScriptVersion, redeemScript, mgr.ChainParams())
	if err != nil {
		return newError(ErrTxSigning, "unparseable redeem script", err)
	}
	if class != txscript.MultiSigTy {
		return newError(ErrTxSigning, fmt.Sprintf("redeem script is not multi-sig: %v", class), nil)
	}
	if len(sigs) < nRequired {
		errStr := fmt.Sprintf("not enough signatures; need %d but got only %d", nRequired,
			len(sigs))
		return newError(ErrTxSigning, errStr, nil)
	}

	// Construct the unlocking script.
	// Start with an OP_0 because of the bug in bitcoind, then add nRequired signatures.
	unlockingScript := txscript.NewScriptBuilder().AddOp(txscript.OP_FALSE)
	for _, sig := range sigs[:nRequired] {
		unlockingScript.AddData(sig)
	}

	// Combine the redeem script and the unlocking script to get the actual signature script.
	sigScript := unlockingScript.AddData(redeemScript)
	script, err := sigScript.Script()
	if err != nil {
		return newError(ErrTxSigning, "error building sigscript", err)
	}
	tx.TxIn[idx].SignatureScript = script

	if err := validateSigScript(tx, idx, pkScript); err != nil {
		return err
	}
	return nil
}
Пример #7
0
// GetSSGenStakeOutputInfo takes an SSGen tx as input and scans through its
// outputs, returning the amount of the output and the PKH or SH that it was
// sent to.
func GetSSGenStakeOutputInfo(tx *dcrutil.Tx, params *chaincfg.Params) ([]bool,
	[][]byte, []int64, error) {
	msgTx := tx.MsgTx()
	numOutputsInSSGen := len(msgTx.TxOut)

	isP2SH := make([]bool, numOutputsInSSGen-2)
	addresses := make([][]byte, numOutputsInSSGen-2)
	amounts := make([]int64, numOutputsInSSGen-2)

	// Cycle through the inputs and generate
	for idx, out := range msgTx.TxOut {
		// We only care about the outputs where we get proportional
		// amounts and the PKHs they were sent to.
		if (idx > 1) && (idx < numOutputsInSSGen) {
			// Get the PKH or SH it's going to, and what type of
			// script it is.
			class, addr, _, err :=
				txscript.ExtractPkScriptAddrs(out.Version, out.PkScript, params)
			if err != nil {
				return nil, nil, nil, err
			}
			if class != txscript.StakeGenTy {
				return nil, nil, nil, fmt.Errorf("ssgen output included non "+
					"ssgen tagged output in idx %v", idx)
			}
			subClass, err := txscript.GetStakeOutSubclass(out.PkScript)
			if !(subClass == txscript.PubKeyHashTy ||
				subClass == txscript.ScriptHashTy) {
				return nil, nil, nil, fmt.Errorf("bad script type")
			}
			isP2SH[idx-2] = false
			if subClass == txscript.ScriptHashTy {
				isP2SH[idx-2] = true
			}

			// Get the amount that was sent.
			amt := out.Value
			addresses[idx-2] = addr[0].ScriptAddress()
			amounts[idx-2] = amt
		}
	}

	return isP2SH, addresses, amounts, nil
}
Пример #8
0
func lookupOutputChain(dbtx walletdb.ReadTx, w *Wallet, details *wtxmgr.TxDetails,
	cred wtxmgr.CreditRecord) (account uint32, internal bool) {

	addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)

	output := details.MsgTx.TxOut[cred.Index]
	_, addrs, _, err := txscript.ExtractPkScriptAddrs(output.Version, output.PkScript, w.chainParams)
	var ma waddrmgr.ManagedAddress
	if err == nil && len(addrs) > 0 {
		ma, err = w.Manager.Address(addrmgrNs, addrs[0])
	}
	if err != nil {
		log.Errorf("Cannot fetch account for wallet output: %v", err)
	} else {
		account = ma.Account()
		internal = ma.Internal()
	}
	return
}
Пример #9
0
// AddTicket adds a ticket transaction to the wallet.
func (w *Wallet) AddTicket(ticket *dcrutil.Tx) error {
	return walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
		stakemgrNs := tx.ReadWriteBucket(wstakemgrNamespaceKey)

		// Insert the ticket to be tracked and voted.
		err := w.StakeMgr.InsertSStx(stakemgrNs, ticket, w.VoteBits)
		if err != nil {
			return err
		}

		if w.stakePoolEnabled {
			addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)

			// Pluck the ticketaddress to indentify the stakepool user.
			pkVersion := ticket.MsgTx().TxOut[0].Version
			pkScript := ticket.MsgTx().TxOut[0].PkScript
			_, addrs, _, err := txscript.ExtractPkScriptAddrs(pkVersion,
				pkScript, w.ChainParams())
			if err != nil {
				return err
			}

			ticketHash := ticket.MsgTx().TxSha()

			chainClient, err := w.requireChainClient()
			if err != nil {
				return err
			}
			rawTx, err := chainClient.GetRawTransactionVerbose(&ticketHash)
			if err != nil {
				return err
			}

			// Update the pool ticket stake. This will include removing it from the
			// invalid slice and adding a ImmatureOrLive ticket to the valid ones.
			err = w.updateStakePoolInvalidTicket(stakemgrNs, addrmgrNs, addrs[0], &ticketHash, rawTx.BlockHeight)
			if err != nil {
				return err
			}
		}
		return nil
	})
}
Пример #10
0
// indexUnconfirmedAddresses modifies the unconfirmed (memory-only) address
// index to include mappings for the addresses encoded by the passed public key
// script to the transaction.
//
// This function is safe for concurrent access.
func (idx *AddrIndex) indexUnconfirmedAddresses(scriptVersion uint16, pkScript []byte, tx *dcrutil.Tx, isSStx bool) {
	// The error is ignored here since the only reason it can fail is if the
	// script fails to parse and it was already validated before being
	// admitted to the mempool.
	class, addresses, _, _ := txscript.ExtractPkScriptAddrs(scriptVersion,
		pkScript, idx.chainParams)

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

		addresses = append(addresses, addr)
	}

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

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

		// Add a mapping from the transaction to the address.
		addrsByTxEntry := idx.addrsByTx[*tx.Sha()]
		if addrsByTxEntry == nil {
			addrsByTxEntry = make(map[[addrKeySize]byte]struct{})
			idx.addrsByTx[*tx.Sha()] = addrsByTxEntry
		}
		addrsByTxEntry[addrKey] = struct{}{}
		idx.unconfirmedLock.Unlock()
	}
}
Пример #11
0
// indexPkScript extracts all standard addresses from the passed public key
// script and maps each of them to the associated transaction using the passed
// map.
func (idx *AddrIndex) indexPkScript(data writeIndexData, scriptVersion uint16, pkScript []byte, txIdx int, isSStx bool) {
	// Nothing to index if the script is non-standard or otherwise doesn't
	// contain any addresses.
	class, addrs, _, err := txscript.ExtractPkScriptAddrs(scriptVersion, pkScript,
		idx.chainParams)
	if err != nil {
		return
	}

	if isSStx && class == txscript.NullDataTy {
		addr, err := stake.AddrFromSStxPkScrCommitment(pkScript, idx.chainParams)
		if err != nil {
			return
		}

		addrs = append(addrs, addr)
	}

	if len(addrs) == 0 {
		return
	}

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

		// Avoid inserting the transaction more than once.  Since the
		// transactions are indexed serially any duplicates will be
		// indexed in a row, so checking the most recent entry for the
		// address is enough to detect duplicates.
		indexedTxns := data[addrKey]
		numTxns := len(indexedTxns)
		if numTxns > 0 && indexedTxns[numTxns-1] == txIdx {
			continue
		}
		indexedTxns = append(indexedTxns, txIdx)
		data[addrKey] = indexedTxns
	}
}
Пример #12
0
// TxSSRtxStakeOutputInfo takes an SSRtx tx as input and scans through its
// outputs, returning the amount of the output and the pkh that it was sent to.
func TxSSRtxStakeOutputInfo(tx *wire.MsgTx, params *chaincfg.Params) ([]bool,
	[][]byte, []int64, error) {
	numOutputsInSSRtx := len(tx.TxOut)

	isP2SH := make([]bool, numOutputsInSSRtx)
	addresses := make([][]byte, numOutputsInSSRtx)
	amounts := make([]int64, numOutputsInSSRtx)

	// Cycle through the inputs and generate
	for idx, out := range tx.TxOut {
		// Get the PKH or SH it's going to, and what type of
		// script it is.
		class, addr, _, err :=
			txscript.ExtractPkScriptAddrs(out.Version, out.PkScript, params)
		if err != nil {
			return nil, nil, nil, err
		}
		if class != txscript.StakeRevocationTy {
			return nil, nil, nil, fmt.Errorf("ssrtx output included non "+
				"ssrtx tagged output in idx %v", idx)
		}
		subClass, err := txscript.GetStakeOutSubclass(out.PkScript)
		if !(subClass == txscript.PubKeyHashTy ||
			subClass == txscript.ScriptHashTy) {
			return nil, nil, nil, fmt.Errorf("bad script type")
		}
		isP2SH[idx] = false
		if subClass == txscript.ScriptHashTy {
			isP2SH[idx] = true
		}

		// Get the amount that was sent.
		amt := out.Value

		addresses[idx] = addr[0].ScriptAddress()
		amounts[idx] = amt
	}

	return isP2SH, addresses, amounts, nil
}
Пример #13
0
func totalBalances(dbtx walletdb.ReadTx, w *Wallet, m map[uint32]dcrutil.Amount) error {
	addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
	unspent, err := w.TxStore.UnspentOutputs(dbtx.ReadBucket(wtxmgrNamespaceKey))
	if err != nil {
		return err
	}
	for i := range unspent {
		output := unspent[i]
		var outputAcct uint32
		_, addrs, _, err := txscript.ExtractPkScriptAddrs(
			txscript.DefaultScriptVersion, output.PkScript, w.chainParams)
		if err == nil && len(addrs) > 0 {
			outputAcct, err = w.Manager.AddrAccount(addrmgrNs, addrs[0])
		}
		if err == nil {
			_, ok := m[outputAcct]
			if ok {
				m[outputAcct] += output.Amount
			}
		}
	}
	return nil
}
Пример #14
0
// groupCreditsByAddr converts a slice of credits to a map from the string
// representation of an encoded address to the unspent outputs associated with
// that address.
func groupCreditsByAddr(credits []*wtxmgr.Credit, chainParams *chaincfg.Params) (
	map[string][]wtxmgr.Credit, error) {
	addrMap := make(map[string][]wtxmgr.Credit)
	for _, c := range credits {
		_, addrs, _, err := txscript.ExtractPkScriptAddrs(txscript.DefaultScriptVersion, c.PkScript, chainParams)
		if err != nil {
			return nil, newError(ErrInputSelection, "failed to obtain input address", err)
		}
		// As our credits are all P2SH we should never have more than one
		// address per credit, so let's error out if that assumption is
		// violated.
		if len(addrs) != 1 {
			return nil, newError(ErrInputSelection, "input doesn't have exactly one address", nil)
		}
		encAddr := addrs[0].EncodeAddress()
		if v, ok := addrMap[encAddr]; ok {
			addrMap[encAddr] = append(v, *c)
		} else {
			addrMap[encAddr] = []wtxmgr.Credit{*c}
		}
	}

	return addrMap, nil
}
Пример #15
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
}
Пример #16
0
// convertToAddrIndex indexes all data pushes greater than 8 bytes within the
// passed SPK and returns a TxAddrIndex with the given data. Our "address"
// index is actually a hash160 index, where in the ideal case the data push
// is either the hash160 of a publicKey (P2PKH) or a Script (P2SH).
func convertToAddrIndex(scrVersion uint16, scr []byte, height int64,
	locInBlock *wire.TxLoc, txType stake.TxType) ([]*database.TxAddrIndex, error) {
	var tais []*database.TxAddrIndex

	if scr == nil || locInBlock == nil {
		return nil, fmt.Errorf("passed nil pointer")
	}

	var indexKey [ripemd160.Size]byte

	// Get the script classes and extract the PKH if applicable.
	// If it's multisig, unknown, etc, just hash the script itself.
	class, addrs, _, err := txscript.ExtractPkScriptAddrs(scrVersion, scr,
		activeNetParams.Params)
	if err != nil {
		return nil, fmt.Errorf("script conversion error: %v", err.Error())
	}
	knownType := false
	for _, addr := range addrs {
		switch {
		case class == txscript.PubKeyTy:
			copy(indexKey[:], addr.Hash160()[:])
		case class == txscript.PubkeyAltTy:
			copy(indexKey[:], addr.Hash160()[:])
		case class == txscript.PubKeyHashTy:
			copy(indexKey[:], addr.ScriptAddress()[:])
		case class == txscript.PubkeyHashAltTy:
			copy(indexKey[:], addr.ScriptAddress()[:])
		case class == txscript.StakeSubmissionTy:
			copy(indexKey[:], addr.ScriptAddress()[:])
		case class == txscript.StakeGenTy:
			copy(indexKey[:], addr.ScriptAddress()[:])
		case class == txscript.StakeRevocationTy:
			copy(indexKey[:], addr.ScriptAddress()[:])
		case class == txscript.StakeSubChangeTy:
			copy(indexKey[:], addr.ScriptAddress()[:])
		case class == txscript.MultiSigTy:
			copy(indexKey[:], addr.ScriptAddress()[:])
		case class == txscript.ScriptHashTy:
			copy(indexKey[:], addr.ScriptAddress()[:])
		}

		tai := &database.TxAddrIndex{
			Hash160:  indexKey,
			Height:   uint32(height),
			TxOffset: uint32(locInBlock.TxStart),
			TxLen:    uint32(locInBlock.TxLen),
		}

		tais = append(tais, tai)
		knownType = true
	}

	// This is a commitment for a future vote or
	// revocation. Extract the address data from
	// it and store it in the addrIndex.
	if txType == stake.TxTypeSStx && class == txscript.NullDataTy {
		addr, err := stake.AddrFromSStxPkScrCommitment(scr,
			activeNetParams.Params)
		if err != nil {
			return nil, fmt.Errorf("ticket commit pkscr conversion error: %v",
				err.Error())
		}

		copy(indexKey[:], addr.ScriptAddress()[:])
		tai := &database.TxAddrIndex{
			Hash160:  indexKey,
			Height:   uint32(height),
			TxOffset: uint32(locInBlock.TxStart),
			TxLen:    uint32(locInBlock.TxLen),
		}

		tais = append(tais, tai)
	} else if !knownType {
		copy(indexKey[:], dcrutil.Hash160(scr))
		tai := &database.TxAddrIndex{
			Hash160:  indexKey,
			Height:   uint32(height),
			TxOffset: uint32(locInBlock.TxStart),
			TxLen:    uint32(locInBlock.TxLen),
		}

		tais = append(tais, tai)
	}

	return tais, nil
}
Пример #17
0
// TestExtractPkScriptAddrs ensures that extracting the type, addresses, and
// number of required signatures from PkScripts works as intended.
func TestExtractPkScriptAddrs(t *testing.T) {
	t.Parallel()

	tests := []struct {
		name    string
		script  []byte
		addrs   []dcrutil.Address
		reqSigs int
		class   txscript.ScriptClass
	}{
		{
			name: "standard p2pk with compressed pubkey (0x02)",
			script: decodeHex("2102192d74d0cb94344c9569c2e7790157" +
				"3d8d7903c3ebec3a957724895dca52c6b4ac"),
			addrs: []dcrutil.Address{
				newAddressPubKey(decodeHex("02192d74d0cb94344" +
					"c9569c2e77901573d8d7903c3ebec3a95772" +
					"4895dca52c6b4")),
			},
			reqSigs: 1,
			class:   txscript.PubKeyTy,
		},
		{
			name: "standard p2pk with uncompressed pubkey (0x04)",
			script: decodeHex("410411db93e1dcdb8a016b49840f8c53bc" +
				"1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb" +
				"84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643" +
				"f656b412a3ac"),
			addrs: []dcrutil.Address{
				newAddressPubKey(decodeHex("0411db93e1dcdb8a0" +
					"16b49840f8c53bc1eb68a382e97b1482ecad" +
					"7b148a6909a5cb2e0eaddfb84ccf9744464f" +
					"82e160bfa9b8b64f9d4c03f999b8643f656b" +
					"412a3")),
			},
			reqSigs: 1,
			class:   txscript.PubKeyTy,
		},
		{
			name: "standard p2pk with hybrid pubkey (0x06)",
			script: decodeHex("4106192d74d0cb94344c9569c2e7790157" +
				"3d8d7903c3ebec3a957724895dca52c6b40d45264838" +
				"c0bd96852662ce6a847b197376830160c6d2eb5e6a4c" +
				"44d33f453eac"),
			addrs: []dcrutil.Address{
				newAddressPubKey(decodeHex("06192d74d0cb94344" +
					"c9569c2e77901573d8d7903c3ebec3a95772" +
					"4895dca52c6b40d45264838c0bd96852662c" +
					"e6a847b197376830160c6d2eb5e6a4c44d33" +
					"f453e")),
			},
			reqSigs: 1,
			class:   txscript.PubKeyTy,
		},
		{
			name: "standard p2pk with compressed pubkey (0x03)",
			script: decodeHex("2103b0bd634234abbb1ba1e986e884185c" +
				"61cf43e001f9137f23c2c409273eb16e65ac"),
			addrs: []dcrutil.Address{
				newAddressPubKey(decodeHex("03b0bd634234abbb1" +
					"ba1e986e884185c61cf43e001f9137f23c2c" +
					"409273eb16e65")),
			},
			reqSigs: 1,
			class:   txscript.PubKeyTy,
		},
		{
			name: "2nd standard p2pk with uncompressed pubkey (0x04)",
			script: decodeHex("4104b0bd634234abbb1ba1e986e884185c" +
				"61cf43e001f9137f23c2c409273eb16e6537a576782e" +
				"ba668a7ef8bd3b3cfb1edb7117ab65129b8a2e681f3c" +
				"1e0908ef7bac"),
			addrs: []dcrutil.Address{
				newAddressPubKey(decodeHex("04b0bd634234abbb1" +
					"ba1e986e884185c61cf43e001f9137f23c2c" +
					"409273eb16e6537a576782eba668a7ef8bd3" +
					"b3cfb1edb7117ab65129b8a2e681f3c1e090" +
					"8ef7b")),
			},
			reqSigs: 1,
			class:   txscript.PubKeyTy,
		},
		{
			name: "standard p2pk with hybrid pubkey (0x07)",
			script: decodeHex("4107b0bd634234abbb1ba1e986e884185c" +
				"61cf43e001f9137f23c2c409273eb16e6537a576782e" +
				"ba668a7ef8bd3b3cfb1edb7117ab65129b8a2e681f3c" +
				"1e0908ef7bac"),
			addrs: []dcrutil.Address{
				newAddressPubKey(decodeHex("07b0bd634234abbb1" +
					"ba1e986e884185c61cf43e001f9137f23c2c" +
					"409273eb16e6537a576782eba668a7ef8bd3" +
					"b3cfb1edb7117ab65129b8a2e681f3c1e090" +
					"8ef7b")),
			},
			reqSigs: 1,
			class:   txscript.PubKeyTy,
		},
		{
			name: "standard p2pkh",
			script: decodeHex("76a914ad06dd6ddee55cbca9a9e3713bd7" +
				"587509a3056488ac"),
			addrs: []dcrutil.Address{
				newAddressPubKeyHash(decodeHex("ad06dd6ddee55" +
					"cbca9a9e3713bd7587509a30564")),
			},
			reqSigs: 1,
			class:   txscript.PubKeyHashTy,
		},
		{
			name: "standard p2sh",
			script: decodeHex("a91463bcc565f9e68ee0189dd5cc67f1b0" +
				"e5f02f45cb87"),
			addrs: []dcrutil.Address{
				newAddressScriptHash(decodeHex("63bcc565f9e68" +
					"ee0189dd5cc67f1b0e5f02f45cb")),
			},
			reqSigs: 1,
			class:   txscript.ScriptHashTy,
		},
		// from real tx 60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1, vout 0
		{
			name: "standard 1 of 2 multisig",
			script: decodeHex("514104cc71eb30d653c0c3163990c47b97" +
				"6f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473" +
				"e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11" +
				"fcdd0d348ac4410461cbdcc5409fb4b4d42b51d33381" +
				"354d80e550078cb532a34bfa2fcfdeb7d76519aecc62" +
				"770f5b0e4ef8551946d8a540911abe3e7854a26f39f5" +
				"8b25c15342af52ae"),
			addrs: []dcrutil.Address{
				newAddressPubKey(decodeHex("04cc71eb30d653c0c" +
					"3163990c47b976f3fb3f37cccdcbedb169a1" +
					"dfef58bbfbfaff7d8a473e7e2e6d317b87ba" +
					"fe8bde97e3cf8f065dec022b51d11fcdd0d3" +
					"48ac4")),
				newAddressPubKey(decodeHex("0461cbdcc5409fb4b" +
					"4d42b51d33381354d80e550078cb532a34bf" +
					"a2fcfdeb7d76519aecc62770f5b0e4ef8551" +
					"946d8a540911abe3e7854a26f39f58b25c15" +
					"342af")),
			},
			reqSigs: 1,
			class:   txscript.MultiSigTy,
		},
		// from real tx d646f82bd5fbdb94a36872ce460f97662b80c3050ad3209bef9d1e398ea277ab, vin 1
		{
			name: "standard 2 of 3 multisig",
			script: decodeHex("524104cb9c3c222c5f7a7d3b9bd152f363" +
				"a0b6d54c9eb312c4d4f9af1e8551b6c421a6a4ab0e29" +
				"105f24de20ff463c1c91fcf3bf662cdde4783d4799f7" +
				"87cb7c08869b4104ccc588420deeebea22a7e900cc8b" +
				"68620d2212c374604e3487ca08f1ff3ae12bdc639514" +
				"d0ec8612a2d3c519f084d9a00cbbe3b53d071e9b09e7" +
				"1e610b036aa24104ab47ad1939edcb3db65f7fedea62" +
				"bbf781c5410d3f22a7a3a56ffefb2238af8627363bdf" +
				"2ed97c1f89784a1aecdb43384f11d2acc64443c7fc29" +
				"9cef0400421a53ae"),
			addrs: []dcrutil.Address{
				newAddressPubKey(decodeHex("04cb9c3c222c5f7a7" +
					"d3b9bd152f363a0b6d54c9eb312c4d4f9af1" +
					"e8551b6c421a6a4ab0e29105f24de20ff463" +
					"c1c91fcf3bf662cdde4783d4799f787cb7c0" +
					"8869b")),
				newAddressPubKey(decodeHex("04ccc588420deeebe" +
					"a22a7e900cc8b68620d2212c374604e3487c" +
					"a08f1ff3ae12bdc639514d0ec8612a2d3c51" +
					"9f084d9a00cbbe3b53d071e9b09e71e610b0" +
					"36aa2")),
				newAddressPubKey(decodeHex("04ab47ad1939edcb3" +
					"db65f7fedea62bbf781c5410d3f22a7a3a56" +
					"ffefb2238af8627363bdf2ed97c1f89784a1" +
					"aecdb43384f11d2acc64443c7fc299cef040" +
					"0421a")),
			},
			reqSigs: 2,
			class:   txscript.MultiSigTy,
		},

		// The below are nonstandard script due to things such as
		// invalid pubkeys, failure to parse, and not being of a
		// standard form.

		{
			name: "p2pk with uncompressed pk missing OP_CHECKSIG",
			script: decodeHex("410411db93e1dcdb8a016b49840f8c53bc" +
				"1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb" +
				"84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643" +
				"f656b412a3"),
			addrs:   nil,
			reqSigs: 0,
			class:   txscript.NonStandardTy,
		},
		{
			name: "valid signature from a sigscript - no addresses",
			script: decodeHex("47304402204e45e16932b8af514961a1d3" +
				"a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220" +
				"181522ec8eca07de4860a4acdd12909d831cc56cbbac" +
				"4622082221a8768d1d0901"),
			addrs:   nil,
			reqSigs: 0,
			class:   txscript.NonStandardTy,
		},
		// Note the technically the pubkey is the second item on the
		// stack, but since the address extraction intentionally only
		// works with standard PkScripts, this should not return any
		// addresses.
		{
			name: "valid sigscript to reedeem p2pk - no addresses",
			script: decodeHex("493046022100ddc69738bf2336318e4e04" +
				"1a5a77f305da87428ab1606f023260017854350ddc02" +
				"2100817af09d2eec36862d16009852b7e3a0f6dd7659" +
				"8290b7834e1453660367e07a014104cd4240c198e125" +
				"23b6f9cb9f5bed06de1ba37e96a1bbd13745fcf9d11c" +
				"25b1dff9a519675d198804ba9962d3eca2d5937d58e5" +
				"a75a71042d40388a4d307f887d"),
			addrs:   nil,
			reqSigs: 0,
			class:   txscript.NonStandardTy,
		},
		// from real tx 691dd277dc0e90a462a3d652a1171686de49cf19067cd33c7df0392833fb986a, vout 0
		// invalid public keys
		{
			name: "1 of 3 multisig with invalid pubkeys",
			script: decodeHex("51411c2200007353455857696b696c6561" +
				"6b73204361626c6567617465204261636b75700a0a63" +
				"61626c65676174652d3230313031323034313831312e" +
				"377a0a0a446f41776e6c6f61642074686520666f6c6c" +
				"6f77696e67207472616e73616374696f6e7320776974" +
				"68205361746f736869204e616b616d6f746f27732064" +
				"6f776e6c6f61416420746f6f6c2077686963680a6361" +
				"6e20626520666f756e6420696e207472616e73616374" +
				"696f6e20366335336364393837313139656637393764" +
				"35616463636453ae"),
			addrs:   []dcrutil.Address{},
			reqSigs: 1,
			class:   txscript.MultiSigTy,
		},
		// from real tx: 691dd277dc0e90a462a3d652a1171686de49cf19067cd33c7df0392833fb986a, vout 44
		// invalid public keys
		{
			name: "1 of 3 multisig with invalid pubkeys 2",
			script: decodeHex("5141346333656332353963373464616365" +
				"36666430383862343463656638630a63363662633139" +
				"39366338623934613338313162333635363138666531" +
				"65396231623541366361636365393933613339383861" +
				"34363966636336643664616266640a32363633636661" +
				"39636634633033633630396335393363336539316665" +
				"64653730323921313233646434326432353633396433" +
				"38613663663530616234636434340a00000053ae"),
			addrs:   []dcrutil.Address{},
			reqSigs: 1,
			class:   txscript.MultiSigTy,
		},
		{
			name:    "empty script",
			script:  []byte{},
			addrs:   nil,
			reqSigs: 0,
			class:   txscript.NonStandardTy,
		},
		{
			name:    "script that does not parse",
			script:  []byte{txscript.OP_DATA_45},
			addrs:   nil,
			reqSigs: 0,
			class:   txscript.NonStandardTy,
		},
	}

	t.Logf("Running %d tests.", len(tests))
	for i, test := range tests {
		class, addrs, reqSigs, err := txscript.ExtractPkScriptAddrs(
			txscript.DefaultScriptVersion, test.script, &chaincfg.MainNetParams)
		if err != nil {
		}

		if !reflect.DeepEqual(addrs, test.addrs) {
			t.Errorf("ExtractPkScriptAddrs #%d (%s) unexpected "+
				"addresses\ngot  %v\nwant %v", i, test.name,
				addrs, test.addrs)
			continue
		}

		if reqSigs != test.reqSigs {
			t.Errorf("ExtractPkScriptAddrs #%d (%s) unexpected "+
				"number of required signatures - got %d, "+
				"want %d", i, test.name, reqSigs, test.reqSigs)
			continue
		}

		if class != test.class {
			t.Errorf("ExtractPkScriptAddrs #%d (%s) unexpected "+
				"script type - got %s, want %s", i, test.name,
				class, test.class)
			continue
		}
	}
}
Пример #18
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
}
Пример #19
0
// testAddrIndexOperations ensures that all normal operations concerning
// the optional address index function correctly.
func testAddrIndexOperations(t *testing.T, db database.Db, newestBlock *dcrutil.Block, newestSha *chainhash.Hash, newestBlockIdx int64) {
	// Metadata about the current addr index state should be unset.
	sha, height, err := db.FetchAddrIndexTip()
	if err != database.ErrAddrIndexDoesNotExist {
		t.Fatalf("Address index metadata shouldn't be in db, hasn't been built up yet.")
	}

	var zeroHash chainhash.Hash
	if !sha.IsEqual(&zeroHash) {
		t.Fatalf("AddrIndexTip wrong hash got: %s, want %s", sha, &zeroHash)

	}

	if height != -1 {
		t.Fatalf("Addrindex not built up, yet a block index tip has been set to: %d.", height)
	}

	// Test enforcement of constraints for "limit" and "skip"
	var fakeAddr dcrutil.Address
	_, err = db.FetchTxsForAddr(fakeAddr, -1, 0)
	if err == nil {
		t.Fatalf("Negative value for skip passed, should return an error")
	}

	_, err = db.FetchTxsForAddr(fakeAddr, 0, -1)
	if err == nil {
		t.Fatalf("Negative value for limit passed, should return an error")
	}

	// Simple test to index outputs(s) of the first tx.
	testIndex := make(database.BlockAddrIndex, database.AddrIndexKeySize)
	testTx, err := newestBlock.Tx(0)
	if err != nil {
		t.Fatalf("Block has no transactions, unable to test addr "+
			"indexing, err %v", err)
	}

	// Extract the dest addr from the tx.
	_, testAddrs, _, err := txscript.ExtractPkScriptAddrs(testTx.MsgTx().TxOut[0].Version, testTx.MsgTx().TxOut[0].PkScript, &chaincfg.MainNetParams)
	if err != nil {
		t.Fatalf("Unable to decode tx output, err %v", err)
	}

	// Extract the hash160 from the output script.
	var hash160Bytes [ripemd160.Size]byte
	testHash160 := testAddrs[0].(*dcrutil.AddressScriptHash).Hash160()
	copy(hash160Bytes[:], testHash160[:])

	// Create a fake index.
	blktxLoc, _, _ := newestBlock.TxLoc()
	testIndex = []*database.TxAddrIndex{
		&database.TxAddrIndex{
			Hash160:  hash160Bytes,
			Height:   uint32(newestBlockIdx),
			TxOffset: uint32(blktxLoc[0].TxStart),
			TxLen:    uint32(blktxLoc[0].TxLen),
		},
	}

	// Insert our test addr index into the DB.
	err = db.UpdateAddrIndexForBlock(newestSha, newestBlockIdx, testIndex)
	if err != nil {
		t.Fatalf("UpdateAddrIndexForBlock: failed to index"+
			" addrs for block #%d (%s) "+
			"err %v", newestBlockIdx, newestSha, err)
	}

	// Chain Tip of address should've been updated.
	assertAddrIndexTipIsUpdated(db, t, newestSha, newestBlockIdx)

	// Check index retrieval.
	txReplies, err := db.FetchTxsForAddr(testAddrs[0], 0, 1000)
	if err != nil {
		t.Fatalf("FetchTxsForAddr failed to correctly fetch txs for an "+
			"address, err %v", err)
	}
	// Should have one reply.
	if len(txReplies) != 1 {
		t.Fatalf("Failed to properly index tx by address.")
	}

	// Our test tx and indexed tx should have the same sha.
	indexedTx := txReplies[0]
	if !bytes.Equal(indexedTx.Sha.Bytes(), testTx.Sha().Bytes()) {
		t.Fatalf("Failed to fetch proper indexed tx. Expected sha %v, "+
			"fetched %v", testTx.Sha(), indexedTx.Sha)
	}

	// Shut down DB.
	db.Sync()
	db.Close()

	// Re-Open, tip still should be updated to current height and sha.
	db, err = database.OpenDB("leveldb", "tstdbopmode")
	if err != nil {
		t.Fatalf("Unable to re-open created db, err %v", err)
	}
	assertAddrIndexTipIsUpdated(db, t, newestSha, newestBlockIdx)

	// Delete the entire index.
	err = db.PurgeAddrIndex()
	if err != nil {
		t.Fatalf("Couldn't delete address index, err %v", err)
	}

	// Former index should no longer exist.
	txReplies, err = db.FetchTxsForAddr(testAddrs[0], 0, 1000)
	if err != nil {
		t.Fatalf("Unable to fetch transactions for address: %v", err)
	}
	if len(txReplies) != 0 {
		t.Fatalf("Address index was not successfully deleted. "+
			"Should have 0 tx's indexed, %v were returned.",
			len(txReplies))
	}

	// Tip should be blanked out.
	if _, _, err := db.FetchAddrIndexTip(); err != database.ErrAddrIndexDoesNotExist {
		t.Fatalf("Address index was not fully deleted.")
	}

}
Пример #20
0
// convertToAddrIndex indexes all data pushes greater than 8 bytes within the
// passed SPK and returns a TxAddrIndex with the given data. Our "address"
// index is actually a hash160 index, where in the ideal case the data push
// is either the hash160 of a publicKey (P2PKH) or a Script (P2SH).
func convertToAddrIndex(scrVersion uint16, scr []byte, height int64,
	locInBlock *wire.TxLoc) ([]*database.TxAddrIndex, error) {
	var tais []*database.TxAddrIndex

	if scr == nil || locInBlock == nil {
		return nil, fmt.Errorf("passed nil pointer")
	}

	var indexKey [ripemd160.Size]byte

	// Get the script classes and extract the PKH if applicable.
	// If it's multisig, unknown, etc, just hash the script itself.
	class, addrs, _, err := txscript.ExtractPkScriptAddrs(scrVersion, scr,
		activeNetParams.Params)
	if err != nil {
		return nil, fmt.Errorf("script conversion error: %v", err.Error())
	}
	knownType := false
	for _, addr := range addrs {
		switch {
		case class == txscript.PubKeyTy:
			copy(indexKey[:], addr.Hash160()[:])
		case class == txscript.PubkeyAltTy:
			copy(indexKey[:], addr.Hash160()[:])
		case class == txscript.PubKeyHashTy:
			copy(indexKey[:], addr.ScriptAddress()[:])
		case class == txscript.PubkeyHashAltTy:
			copy(indexKey[:], addr.ScriptAddress()[:])
		case class == txscript.StakeSubmissionTy:
			copy(indexKey[:], addr.ScriptAddress()[:])
		case class == txscript.StakeGenTy:
			copy(indexKey[:], addr.ScriptAddress()[:])
		case class == txscript.StakeRevocationTy:
			copy(indexKey[:], addr.ScriptAddress()[:])
		case class == txscript.StakeSubChangeTy:
			copy(indexKey[:], addr.ScriptAddress()[:])
		case class == txscript.MultiSigTy:
			copy(indexKey[:], addr.ScriptAddress()[:])
		case class == txscript.ScriptHashTy:
			copy(indexKey[:], addr.ScriptAddress()[:])
		}
		tai := &database.TxAddrIndex{
			indexKey,
			uint32(height),
			uint32(locInBlock.TxStart),
			uint32(locInBlock.TxLen),
		}

		tais = append(tais, tai)
		knownType = true
	}

	if !knownType {
		copy(indexKey[:], dcrutil.Hash160(scr))
		tai := &database.TxAddrIndex{
			indexKey,
			uint32(height),
			uint32(locInBlock.TxStart),
			uint32(locInBlock.TxLen),
		}

		tais = append(tais, tai)
	}

	return tais, nil
}
Пример #21
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
}
Пример #22
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{}{}
			}
		}
	}
}
Пример #23
0
// UnspentOutputs fetches all unspent outputs from the wallet that match rules
// described in the passed policy.
func (w *Wallet) UnspentOutputs(policy OutputSelectionPolicy) ([]*TransactionOutput, error) {
	var outputResults []*TransactionOutput
	err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
		addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
		txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)

		_, tipHeight := w.TxStore.MainChainTip(txmgrNs)

		// TODO: actually stream outputs from the db instead of fetching
		// all of them at once.
		outputs, err := w.TxStore.UnspentOutputs(txmgrNs)
		if err != nil {
			return err
		}

		for _, output := range outputs {
			// Ignore outputs that haven't reached the required
			// number of confirmations.
			if !policy.meetsRequiredConfs(output.Height, tipHeight) {
				continue
			}

			// Ignore outputs that are not controlled by the account.
			_, addrs, _, err := txscript.ExtractPkScriptAddrs(
				txscript.DefaultScriptVersion, output.PkScript,
				w.chainParams)
			if err != nil || len(addrs) == 0 {
				// Cannot determine which account this belongs
				// to without a valid address.  TODO: Fix this
				// by saving outputs per account, or accounts
				// per output.
				continue
			}
			outputAcct, err := w.Manager.AddrAccount(addrmgrNs, addrs[0])
			if err != nil {
				return err
			}
			if outputAcct != policy.Account {
				continue
			}

			// Stakebase isn't exposed by wtxmgr so those will be
			// OutputKindNormal for now.
			outputSource := OutputKindNormal
			if output.FromCoinBase {
				outputSource = OutputKindCoinbase
			}

			result := &TransactionOutput{
				OutPoint: output.OutPoint,
				Output: wire.TxOut{
					Value: int64(output.Amount),
					// TODO: version is bogus but there is
					// only version 0 at time of writing.
					Version:  txscript.DefaultScriptVersion,
					PkScript: output.PkScript,
				},
				OutputKind:      outputSource,
				ContainingBlock: BlockIdentity(output.Block),
				ReceiveTime:     output.Received,
			}
			outputResults = append(outputResults, result)
		}

		return nil
	})
	return outputResults, err
}
Пример #24
0
// CoinbasePaysTax checks to see if a given block's coinbase correctly pays
// tax to the developer organization.
func CoinbasePaysTax(tx *dcrutil.Tx, height uint32, voters uint16,
	params *chaincfg.Params) error {
	// Taxes only apply from block 2 onwards.
	if height <= 1 {
		return nil
	}

	// Tax is disabled.
	if params.BlockTaxProportion == 0 {
		return nil
	}

	if len(tx.MsgTx().TxOut) == 0 {
		errStr := fmt.Sprintf("invalid coinbase (no outputs)")
		return ruleError(ErrNoTxOutputs, errStr)
	}

	// Coinbase output 0 must be the subsidy to the dev organization.
	taxPkVersion := tx.MsgTx().TxOut[0].Version
	taxPkScript := tx.MsgTx().TxOut[0].PkScript
	class, addrs, _, err :=
		txscript.ExtractPkScriptAddrs(taxPkVersion, taxPkScript, params)
	// The script can't be a weird class.
	if !(class == txscript.ScriptHashTy ||
		class == txscript.PubKeyHashTy ||
		class == txscript.PubKeyTy) {
		errStr := fmt.Sprintf("wrong script class for tax output")
		return ruleError(ErrNoTax, errStr)
	}

	// There should only be one address.
	if len(addrs) != 1 {
		errStr := fmt.Sprintf("no or too many addresses in output")
		return ruleError(ErrNoTax, errStr)
	}

	// Decode the organization address.
	addrOrg, err := dcrutil.DecodeAddress(params.OrganizationAddress, params)
	if err != nil {
		return err
	}

	if !bytes.Equal(addrs[0].ScriptAddress(), addrOrg.ScriptAddress()) {
		errStr := fmt.Sprintf("address in output 0 has non matching org "+
			"address; got %v (hash160 %x), want %v (hash160 %x)",
			addrs[0].EncodeAddress(),
			addrs[0].ScriptAddress(),
			addrOrg.EncodeAddress(),
			addrOrg.ScriptAddress())
		return ruleError(ErrNoTax, errStr)
	}

	// Get the amount of subsidy that should have been paid out to
	// the organization, then check it.
	orgSubsidy := CalcBlockTaxSubsidy(int64(height), voters, params)
	amountFound := tx.MsgTx().TxOut[0].Value
	if orgSubsidy != amountFound {
		errStr := fmt.Sprintf("amount in output 0 has non matching org "+
			"calculated amount; got %v, want %v", amountFound, orgSubsidy)
		return ruleError(ErrNoTax, errStr)
	}

	return nil
}
Пример #25
0
// BlockOneCoinbasePaysTokens checks to see if the first block coinbase pays
// out to the network initial token ledger.
func BlockOneCoinbasePaysTokens(tx *dcrutil.Tx, params *chaincfg.Params) error {
	// If no ledger is specified, just return true.
	if len(params.BlockOneLedger) == 0 {
		return nil
	}

	if tx.MsgTx().LockTime != 0 {
		errStr := fmt.Sprintf("block 1 coinbase has invalid locktime")
		return ruleError(ErrBlockOneTx, errStr)
	}

	if tx.MsgTx().Expiry != wire.NoExpiryValue {
		errStr := fmt.Sprintf("block 1 coinbase has invalid expiry")
		return ruleError(ErrBlockOneTx, errStr)
	}

	if tx.MsgTx().TxIn[0].Sequence != wire.MaxTxInSequenceNum {
		errStr := fmt.Sprintf("block 1 coinbase not finalized")
		return ruleError(ErrBlockOneInputs, errStr)
	}

	if len(tx.MsgTx().TxOut) == 0 {
		errStr := fmt.Sprintf("coinbase outputs empty in block 1")
		return ruleError(ErrBlockOneOutputs, errStr)
	}

	ledger := params.BlockOneLedger
	if len(ledger) != len(tx.MsgTx().TxOut) {
		errStr := fmt.Sprintf("wrong number of outputs in block 1 coinbase; "+
			"got %v, expected %v", len(tx.MsgTx().TxOut), len(ledger))
		return ruleError(ErrBlockOneOutputs, errStr)
	}

	// Check the addresses and output amounts against those in the ledger.
	for i, txout := range tx.MsgTx().TxOut {
		if txout.Version != txscript.DefaultScriptVersion {
			errStr := fmt.Sprintf("bad block one output version; want %v, got %v",
				txscript.DefaultScriptVersion, txout.Version)
			return ruleError(ErrBlockOneOutputs, errStr)
		}

		// There should only be one address.
		_, addrs, _, err :=
			txscript.ExtractPkScriptAddrs(txout.Version, txout.PkScript, params)
		if len(addrs) != 1 {
			errStr := fmt.Sprintf("too many addresses in output")
			return ruleError(ErrBlockOneOutputs, errStr)
		}

		addrLedger, err := dcrutil.DecodeAddress(ledger[i].Address, params)
		if err != nil {
			return err
		}

		if !bytes.Equal(addrs[0].ScriptAddress(), addrLedger.ScriptAddress()) {
			errStr := fmt.Sprintf("address in output %v has non matching "+
				"address; got %v (hash160 %x), want %v (hash160 %x)",
				i,
				addrs[0].EncodeAddress(),
				addrs[0].ScriptAddress(),
				addrLedger.EncodeAddress(),
				addrLedger.ScriptAddress())
			return ruleError(ErrBlockOneOutputs, errStr)
		}

		if txout.Value != ledger[i].Amount {
			errStr := fmt.Sprintf("address in output %v has non matching "+
				"amount; got %v, want %v", i, txout.Value, ledger[i].Amount)
			return ruleError(ErrBlockOneOutputs, errStr)
		}
	}

	return nil
}