Пример #1
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()
	}
}
Пример #2
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
	}
}
Пример #3
0
// evaluateStakePoolTicket evaluates a stake pool ticket to see if it's
// acceptable to the stake pool. The ticket must pay out to the stake
// pool cold wallet, and must have a sufficient fee.
func (w *Wallet) evaluateStakePoolTicket(rec *wtxmgr.TxRecord,
	block *wtxmgr.BlockMeta, poolUser dcrutil.Address) (bool, error) {
	tx := rec.MsgTx

	// Check the first commitment output (txOuts[1])
	// and ensure that the address found there exists
	// in the list of approved addresses. Also ensure
	// that the fee exists and is of the amount
	// requested by the pool.
	commitmentOut := tx.TxOut[1]
	commitAddr, err := stake.AddrFromSStxPkScrCommitment(
		commitmentOut.PkScript, w.chainParams)
	if err != nil {
		return false, fmt.Errorf("Failed to parse commit out addr: %s",
			err.Error())
	}

	// Extract the fee from the ticket.
	in := dcrutil.Amount(0)
	for i := range tx.TxOut {
		if i%2 != 0 {
			commitAmt, err := stake.AmountFromSStxPkScrCommitment(
				tx.TxOut[i].PkScript)
			if err != nil {
				return false, fmt.Errorf("Failed to parse commit "+
					"out amt for commit in vout %v: %s", i, err.Error())
			}
			in += dcrutil.Amount(commitAmt)
		}
	}
	out := dcrutil.Amount(0)
	for i := range tx.TxOut {
		out += dcrutil.Amount(tx.TxOut[i].Value)
	}
	fees := in - out

	_, exists := w.stakePoolColdAddrs[commitAddr.EncodeAddress()]
	if exists {
		commitAmt, err := stake.AmountFromSStxPkScrCommitment(
			commitmentOut.PkScript)
		if err != nil {
			return false, fmt.Errorf("failed to parse commit "+
				"out amt: %s", err.Error())
		}

		// Calculate the fee required based on the current
		// height and the required amount from the pool.
		feeNeeded := txrules.StakePoolTicketFee(dcrutil.Amount(
			tx.TxOut[0].Value), fees, block.Height, w.PoolFees(),
			w.ChainParams())
		if commitAmt < feeNeeded {
			log.Warnf("User %s submitted ticket %v which "+
				"has less fees than are required to use this "+
				"stake pool and is being skipped (required: %v"+
				", found %v)", commitAddr.EncodeAddress(),
				tx.TxSha(), feeNeeded, commitAmt)

			// Reject the entire transaction if it didn't
			// pay the pool server fees.
			return false, nil
		}
	} else {
		log.Warnf("Unknown pool commitment address %s for ticket %v",
			commitAddr.EncodeAddress(), tx.TxSha())
		return false, nil
	}

	log.Debugf("Accepted valid stake pool ticket %v committing %v in fees",
		tx.TxSha(), tx.TxOut[0].Value)

	return true, nil
}
Пример #4
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{}{}
			}
		}
	}
}
Пример #5
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
}
Пример #6
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
}