示例#1
0
文件: addrindex.go 项目: jrick/btcd
// 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(pkScript []byte, tx *btcutil.Tx) {
	// 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.
	_, addresses, _, _ := txscript.ExtractPkScriptAddrs(pkScript,
		idx.chainParams)
	for _, addr := range addresses {
		// Ignore unsupported address types.
		addrKey, err := addrToKey(addr)
		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]*btcutil.Tx)
			idx.txnsByAddr[addrKey] = addrIndexEntry
		}
		addrIndexEntry[*tx.Hash()] = tx

		// Add a mapping from the transaction to the address.
		addrsByTxEntry := idx.addrsByTx[*tx.Hash()]
		if addrsByTxEntry == nil {
			addrsByTxEntry = make(map[[addrKeySize]byte]struct{})
			idx.addrsByTx[*tx.Hash()] = addrsByTxEntry
		}
		addrsByTxEntry[addrKey] = struct{}{}
		idx.unconfirmedLock.Unlock()
	}
}
示例#2
0
// getActor returns the actor to which this vout belongs to
func (com *Communication) getActor(actors []*Actor,
	vout *wire.TxOut) (*Actor, error) {
	// get addrs which own this utxo
	_, addrs, _, err := txscript.ExtractPkScriptAddrs(vout.PkScript,
		&chaincfg.SimNetParams)
	if err != nil {
		return nil, err
	}

	// we're expecting only 1 addr since we created a standard p2pkh tx
	addr := addrs[0].String()
	// find which actor this addr belongs to
	// TODO: could probably be optimized by creating
	// a global addr -> actor index rather than looking
	// up each actor addrs
	for _, actor := range actors {
		for _, actorAddr := range actor.ownedAddresses {
			if addr == actorAddr.String() {
				return actor, nil
			}
		}
	}
	err = errors.New("cannot find any actor who owns this tx output")
	return nil, err
}
示例#3
0
func lookupInputAccount(w *Wallet, details *wtxmgr.TxDetails, deb wtxmgr.DebitRecord) uint32 {
	// 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(&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.PkScript, w.chainParams)
	var inputAcct uint32
	if err == nil && len(addrs) > 0 {
		inputAcct, err = w.Manager.AddrAccount(addrs[0])
	}
	if err != nil {
		log.Errorf("Cannot fetch account for previous output %v: %v", prevOP, err)
		inputAcct = 0
	}
	return inputAcct
}
示例#4
0
文件: addrindex.go 项目: jrick/btcd
// 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, pkScript []byte, txIdx int) {
	// Nothing to index if the script is non-standard or otherwise doesn't
	// contain any addresses.
	_, addrs, _, err := txscript.ExtractPkScriptAddrs(pkScript,
		idx.chainParams)
	if err != nil || len(addrs) == 0 {
		return
	}

	for _, addr := range addrs {
		addrKey, err := addrToKey(addr)
		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
	}
}
示例#5
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(
		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: [12gpXQVcCL2qhTNQgyLVdCFG2Qs2px98nV]
	// Required Signatures: 1
}
示例#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(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].(*btcutil.AddressScriptHash))
	if err != nil {
		return newError(ErrTxSigning, "unable to retrieve redeem script", err)
	}

	class, _, nRequired, err := txscript.ExtractPkScriptAddrs(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
//this is a debug func
func get_tx_info(tx *wire.MsgTx, block_index uint64) (source,
	destination string, btc_amount, fee uint64, data []string) {
	var bFound bool = false

	for _, value := range tx.TxOut {
		nettype := &chaincfg.MainNetParams
		if conf.MainNet {
			nettype = &chaincfg.MainNetParams
		} else {
			nettype = &chaincfg.RegressionNetParams
		}
		_, Address, _, _ := txscript.ExtractPkScriptAddrs(value.PkScript, nettype)

		if len(Address) != 0 {
			if Address[0].String() == conf.WISHINGWALLADDRESS {

				bFound = true
				continue
			}
		}
		if bFound == true {
			tempasm, _ := txscript.DisasmString(value.PkScript)

			message := strings.Split(tempasm, " ")

			merge := message[1] + message[2]
			data = append(data, merge)
		}
	}
	if bFound == true {
		destination = conf.WISHINGWALLADDRESS
	} else {
		var temp []string
		return "", "", 0, 0, temp
	}

	//get source address

	if tx.TxIn[0].PreviousOutPoint.Index == 0 {
		source = string(block_index)
	} else {
		SourceTx, _ := bitcoinchain.GetRawTransaction(tx.TxIn[0].PreviousOutPoint.Hash.String())

		if SourceTx == nil {
			source = "Unkonw"
		} else {
			for _, prevalue := range SourceTx.Vout {

				if prevalue.N == tx.TxIn[0].PreviousOutPoint.Index {
					source = prevalue.ScriptPubKey.Addresses[0]
				}
			}
		}
	}

	return source, destination, 0, 0, data
}
示例#8
0
func (w *Wallet) findEligibleOutputs(account uint32, minconf int32, bs *waddrmgr.BlockStamp) ([]wtxmgr.Credit, error) {
	unspent, err := w.TxStore.UnspentOutputs()
	if err != nil {
		return nil, err
	}

	// TODO: Eventually all of these filters (except perhaps output locking)
	// should be handled by the call to UnspentOutputs (or similar).
	// Because one of these filters requires matching the output script to
	// the desired account, this change depends on making wtxmgr a waddrmgr
	// dependancy and requesting unspent outputs for a single account.
	eligible := make([]wtxmgr.Credit, 0, len(unspent))
	for i := range unspent {
		output := &unspent[i]

		// Only include this output if it meets the required number of
		// confirmations.  Coinbase transactions must have have reached
		// maturity before their outputs may be spent.
		if !confirmed(minconf, output.Height, bs.Height) {
			continue
		}
		if output.FromCoinBase {
			const target = blockchain.CoinbaseMaturity
			if !confirmed(target, output.Height, bs.Height) {
				continue
			}
		}

		// Locked unspent outputs are skipped.
		if w.LockedOutpoint(output.OutPoint) {
			continue
		}

		// Filter out unspendable outputs, that is, remove those that
		// (at this time) are not P2PKH outputs.  Other inputs must be
		// manually included in transactions and sent (for example,
		// using createrawtransaction, signrawtransaction, and
		// sendrawtransaction).
		class, addrs, _, err := txscript.ExtractPkScriptAddrs(
			output.PkScript, w.chainParams)
		if err != nil || class != txscript.PubKeyHashTy {
			continue
		}

		// Only include the output if it is associated with the passed
		// account.  There should only be one address since this is a
		// P2PKH script.
		addrAcct, err := w.Manager.AddrAccount(addrs[0])
		if err != nil || addrAcct != account {
			continue
		}

		eligible = append(eligible, *output)
	}
	return eligible, nil
}
示例#9
0
func (w *Wallet) findEligibleOutputs(account uint32, minconf int32, bs *waddrmgr.BlockStamp) ([]wtxmgr.Credit, error) {
	unspent, err := w.TxStore.UnspentOutputs()
	if err != nil {
		return nil, err
	}

	// TODO: Eventually all of these filters (except perhaps output locking)
	// should be handled by the call to UnspentOutputs (or similar).
	// Because one of these filters requires matching the output script to
	// the desired account, this change depends on making wtxmgr a waddrmgr
	// dependancy and requesting unspent outputs for a single account.
	eligible := make([]wtxmgr.Credit, 0, len(unspent))
	for i := range unspent {
		output := &unspent[i]

		// Only include this output if it meets the required number of
		// confirmations.  Coinbase transactions must have have reached
		// maturity before their outputs may be spent.
		if !confirmed(minconf, output.Height, bs.Height) {
			continue
		}
		if output.FromCoinBase {
			const target = blockchain.CoinbaseMaturity
			if !confirmed(target, output.Height, bs.Height) {
				continue
			}
		}

		// Locked unspent outputs are skipped.
		if w.LockedOutpoint(output.OutPoint) {
			continue
		}

		// Only include the output if it is associated with the passed
		// account.
		//
		// TODO: Handle multisig outputs by determining if enough of the
		// addresses are controlled.
		_, addrs, _, err := txscript.ExtractPkScriptAddrs(
			output.PkScript, w.chainParams)
		if err != nil || len(addrs) != 1 {
			continue
		}
		addrAcct, err := w.Manager.AddrAccount(addrs[0])
		if err != nil || addrAcct != account {
			continue
		}

		eligible = append(eligible, *output)
	}
	return eligible, nil
}
示例#10
0
func NewOutputRecord(txout *wire.TxOut) *OutputRecord {
	class, addrs, sigs, _ := txscript.ExtractPkScriptAddrs(txout.PkScript,
		&chaincfg.MainNetParams)

	record := &OutputRecord{
		value: txout.Value,
		class: uint8(class),
		sigs:  uint8(sigs),
		addrs: addrs,
	}

	return record
}
示例#11
0
文件: mempool.go 项目: kleetus/btcd
// removeScriptFromAddrIndex dissociates the address encoded by the
// passed pkScript from the passed tx in our address based tx index.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) removeScriptFromAddrIndex(pkScript []byte, tx *btcutil.Tx) error {
	_, addresses, _, err := txscript.ExtractPkScriptAddrs(pkScript,
		activeNetParams.Params)
	if err != nil {
		txmpLog.Errorf("Unable to extract encoded addresses from script "+
			"for addrindex (addrindex): %v", err)
		return err
	}
	for _, addr := range addresses {
		delete(mp.addrindex[addr.EncodeAddress()], *tx.Sha())
	}

	return nil
}
示例#12
0
func lookupOutputChain(w *Wallet, details *wtxmgr.TxDetails, cred wtxmgr.CreditRecord) (account uint32, internal bool) {
	output := details.MsgTx.TxOut[cred.Index]
	_, addrs, _, err := txscript.ExtractPkScriptAddrs(output.PkScript, w.chainParams)
	var ma waddrmgr.ManagedAddress
	if err == nil && len(addrs) > 0 {
		ma, err = w.Manager.Address(addrs[0])
	}
	if err != nil {
		log.Errorf("Cannot fetch account for wallet output: %v", err)
	} else {
		account = ma.Account()
		internal = ma.Internal()
	}
	return
}
示例#13
0
文件: mempool.go 项目: kleetus/btcd
// indexScriptByAddress alters our address index by indexing the payment address
// encoded by the passed scriptPubKey to the passed transaction.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) indexScriptAddressToTx(pkScript []byte, tx *btcutil.Tx) error {
	_, addresses, _, err := txscript.ExtractPkScriptAddrs(pkScript,
		activeNetParams.Params)
	if err != nil {
		txmpLog.Errorf("Unable to extract encoded addresses from script "+
			"for addrindex: %v", err)
		return err
	}

	for _, addr := range addresses {
		if mp.addrindex[addr.EncodeAddress()] == nil {
			mp.addrindex[addr.EncodeAddress()] = make(map[wire.ShaHash]struct{})
		}
		mp.addrindex[addr.EncodeAddress()][*tx.Sha()] = struct{}{}
	}

	return nil
}
示例#14
0
// ListAddressTransactions returns a slice of objects with details about
// recorded transactions to or from any address belonging to a set.  This is
// intended to be used for listaddresstransactions RPC replies.
func (w *Wallet) ListAddressTransactions(pkHashes map[string]struct{}) (
	[]btcjson.ListTransactionsResult, error) {

	txList := []btcjson.ListTransactionsResult{}

	// Get current block.  The block height used for calculating
	// the number of tx confirmations.
	syncBlock := w.Manager.SyncedTo()

	err := w.TxStore.RangeTransactions(0, -1, func(details []wtxmgr.TxDetails) (bool, error) {
	loopDetails:
		for i := range details {
			detail := &details[i]

			for _, cred := range detail.Credits {
				pkScript := detail.MsgTx.TxOut[cred.Index].PkScript
				_, addrs, _, err := txscript.ExtractPkScriptAddrs(
					pkScript, w.chainParams)
				if err != nil || len(addrs) != 1 {
					continue
				}
				apkh, ok := addrs[0].(*btcutil.AddressPubKeyHash)
				if !ok {
					continue
				}
				_, ok = pkHashes[string(apkh.ScriptAddress())]
				if !ok {
					continue
				}

				jsonResults := ListTransactions(detail,
					syncBlock.Height, w.chainParams)
				if err != nil {
					return false, err
				}
				txList = append(txList, jsonResults...)
				continue loopDetails
			}
		}
		return false, nil
	})

	return txList, err
}
示例#15
0
// signMsgTx sets the SignatureScript for every item in msgtx.TxIn.
// It must be called every time a msgtx is changed.
// Only P2PKH outputs are supported at this point.
func signMsgTx(msgtx *wire.MsgTx, prevOutputs []wtxmgr.Credit, mgr *waddrmgr.Manager, chainParams *chaincfg.Params) error {
	if len(prevOutputs) != len(msgtx.TxIn) {
		return fmt.Errorf(
			"Number of prevOutputs (%d) does not match number of tx inputs (%d)",
			len(prevOutputs), len(msgtx.TxIn))
	}
	for i, output := range prevOutputs {
		// Errors don't matter here, as we only consider the
		// case where len(addrs) == 1.
		_, addrs, _, _ := txscript.ExtractPkScriptAddrs(output.PkScript,
			chainParams)
		if len(addrs) != 1 {
			continue
		}
		apkh, ok := addrs[0].(*btcutil.AddressPubKeyHash)
		if !ok {
			return ErrUnsupportedTransactionType
		}

		ai, err := mgr.Address(apkh)
		if err != nil {
			return fmt.Errorf("cannot get address info: %v", err)
		}

		pka := ai.(waddrmgr.ManagedPubKeyAddress)
		privkey, err := pka.PrivKey()
		if err != nil {
			return fmt.Errorf("cannot get private key: %v", err)
		}

		sigscript, err := txscript.SignatureScript(msgtx, i,
			output.PkScript, txscript.SigHashAll, privkey,
			ai.Compressed())
		if err != nil {
			return fmt.Errorf("cannot create sigscript: %s", err)
		}
		msgtx.TxIn[i].SignatureScript = sigscript
	}

	return nil
}
示例#16
0
// addTestTx adds an output spendable by our test wallet, marked as included in
// 'block'.
func addTestTx(w *LightningWallet, rec *wtxmgr.TxRecord, block *wtxmgr.BlockMeta) error {
	err := w.TxStore.InsertTx(rec, block)
	if err != nil {
		return err
	}

	// 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 {
		_, addrs, _, err := txscript.ExtractPkScriptAddrs(output.PkScript,
			ActiveNetParams)
		if err != nil {
			// Non-standard outputs are skipped.
			continue
		}
		for _, addr := range addrs {
			ma, err := w.Manager.Address(addr)
			if err == nil {
				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
				}
				continue
			}

			// Missing addresses are skipped.  Other errors should
			// be propagated.
			if !waddrmgr.IsError(err, waddrmgr.ErrAddressNotFound) {
				return err
			}
		}
	}
	return nil
}
示例#17
0
func totalBalances(w *Wallet, m map[uint32]btcutil.Amount) error {
	unspent, err := w.TxStore.UnspentOutputs()
	if err != nil {
		return err
	}
	for i := range unspent {
		output := &unspent[i]
		var outputAcct uint32
		_, addrs, _, err := txscript.ExtractPkScriptAddrs(
			output.PkScript, w.chainParams)
		if err == nil && len(addrs) > 0 {
			outputAcct, err = w.Manager.AddrAccount(addrs[0])
		}
		if err == nil {
			_, ok := m[outputAcct]
			if ok {
				m[outputAcct] += output.Amount
			}
		}
	}
	return nil
}
示例#18
0
// TotalReceivedForAddr iterates through a wallet's transaction history,
// returning the total amount of bitcoins received for a single wallet
// address.
func (w *Wallet) TotalReceivedForAddr(addr btcutil.Address, minConf int32) (btcutil.Amount, error) {
	syncBlock := w.Manager.SyncedTo()

	var (
		addrStr    = addr.EncodeAddress()
		amount     btcutil.Amount
		stopHeight int32
	)

	if minConf > 0 {
		stopHeight = syncBlock.Height - minConf + 1
	} else {
		stopHeight = -1
	}
	err := w.TxStore.RangeTransactions(0, stopHeight, func(details []wtxmgr.TxDetails) (bool, error) {
		for i := range details {
			detail := &details[i]
			for _, cred := range detail.Credits {
				pkScript := detail.MsgTx.TxOut[cred.Index].PkScript
				_, addrs, _, err := txscript.ExtractPkScriptAddrs(
					pkScript, w.chainParams)
				// An error creating addresses from the output script only
				// indicates a non-standard script, so ignore this credit.
				if err != nil {
					continue
				}
				for _, a := range addrs {
					if addrStr == a.EncodeAddress() {
						amount += cred.Amount
						break
					}
				}
			}
		}
		return false, nil
	})
	return amount, err
}
示例#19
0
文件: tx.go 项目: nikirill/cothority
func NewTxOut(txoutraw []byte) (txout *TxOut, offset int) {
	txout = new(TxOut)
	txout.Value = binary.LittleEndian.Uint64(txoutraw[0:8])
	offset = 8

	pkscript, pkscriptsize := DecodeVariableLengthInteger(txoutraw[offset:])
	offset += pkscriptsize

	txout.Pkscript = txoutraw[offset : offset+pkscript]
	offset += pkscript

	_, addrhash, _, err := txscript.ExtractPkScriptAddrs(txout.Pkscript, &chaincfg.MainNetParams)
	if err != nil {
		return
	}
	if len(addrhash) != 0 {
		txout.Addr = addrhash[0].EncodeAddress()
	} else {
		txout.Addr = ""
	}

	return
}
示例#20
0
// CalculateAccountBalance sums the amounts of all unspent transaction
// outputs to the given account of a wallet and returns the balance.
func (w *Wallet) CalculateAccountBalance(account uint32, confirms int32) (btcutil.Amount, error) {
	var bal btcutil.Amount

	// Get current block.  The block height used for calculating
	// the number of tx confirmations.
	syncBlock := w.Manager.SyncedTo()

	unspent, err := w.TxStore.UnspentOutputs()
	if err != nil {
		return 0, err
	}
	for i := range unspent {
		output := &unspent[i]

		if !confirmed(confirms, output.Height, syncBlock.Height) {
			continue
		}
		if output.FromCoinBase {
			const target = blockchain.CoinbaseMaturity
			if !confirmed(target, output.Height, syncBlock.Height) {
				continue
			}
		}

		var outputAcct uint32
		_, addrs, _, err := txscript.ExtractPkScriptAddrs(
			output.PkScript, w.chainParams)
		if err == nil && len(addrs) > 0 {
			outputAcct, err = w.Manager.AddrAccount(addrs[0])
		}
		if err == nil && outputAcct == account {
			bal += output.Amount
		}
	}
	return bal, nil
}
示例#21
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(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
}
示例#22
0
// TotalReceivedForAccount iterates through a wallet's transaction history,
// returning the total amount of bitcoins received for a single wallet
// account.
func (w *Wallet) TotalReceivedForAccount(account uint32, minConf int32) (btcutil.Amount, int32, error) {
	syncBlock := w.Manager.SyncedTo()

	var (
		amount     btcutil.Amount
		lastConf   int32 // Confs of the last matching transaction.
		stopHeight int32
	)

	if minConf > 0 {
		stopHeight = syncBlock.Height - minConf + 1
	} else {
		stopHeight = -1
	}
	err := w.TxStore.RangeTransactions(0, stopHeight, func(details []wtxmgr.TxDetails) (bool, error) {
		for i := range details {
			detail := &details[i]
			for _, cred := range detail.Credits {
				pkScript := detail.MsgTx.TxOut[cred.Index].PkScript
				var outputAcct uint32
				_, addrs, _, err := txscript.ExtractPkScriptAddrs(
					pkScript, w.chainParams)
				if err == nil && len(addrs) > 0 {
					outputAcct, err = w.Manager.AddrAccount(addrs[0])
				}
				if err == nil && outputAcct == account {
					amount += cred.Amount
					lastConf = confirms(detail.Block.Height, syncBlock.Height)
				}
			}
		}
		return false, nil
	})

	return amount, lastConf, err
}
示例#23
0
// ListUnspent returns a slice of objects representing the unspent wallet
// transactions fitting the given criteria. The confirmations will be more than
// minconf, less than maxconf and if addresses is populated only the addresses
// contained within it will be considered.  If we know nothing about a
// transaction an empty array will be returned.
func (w *Wallet) ListUnspent(minconf, maxconf int32,
	addresses map[string]struct{}) ([]*btcjson.ListUnspentResult, error) {

	syncBlock := w.Manager.SyncedTo()

	filter := len(addresses) != 0

	unspent, err := w.TxStore.UnspentOutputs()
	if err != nil {
		return nil, err
	}
	sort.Sort(sort.Reverse(creditSlice(unspent)))

	defaultAccountName, err := w.Manager.AccountName(waddrmgr.DefaultAccountNum)
	if err != nil {
		return nil, err
	}

	results := make([]*btcjson.ListUnspentResult, 0, len(unspent))
	for i := range unspent {
		output := &unspent[i]

		// Outputs with fewer confirmations than the minimum or more
		// confs than the maximum are excluded.
		confs := confirms(output.Height, syncBlock.Height)
		if confs < minconf || confs > maxconf {
			continue
		}

		// Only mature coinbase outputs are included.
		if output.FromCoinBase {
			const target = blockchain.CoinbaseMaturity
			if !confirmed(target, output.Height, syncBlock.Height) {
				continue
			}
		}

		// Exclude locked outputs from the result set.
		if w.LockedOutpoint(output.OutPoint) {
			continue
		}

		// Lookup the associated account for the output.  Use the
		// default account name in case there is no associated account
		// for some reason, although this should never happen.
		//
		// This will be unnecessary once transactions and outputs are
		// grouped under the associated account in the db.
		acctName := defaultAccountName
		sc, addrs, _, err := txscript.ExtractPkScriptAddrs(
			output.PkScript, w.chainParams)
		if err != nil {
			continue
		}
		if len(addrs) > 0 {
			acct, err := w.Manager.AddrAccount(addrs[0])
			if err == nil {
				s, err := w.Manager.AccountName(acct)
				if err == nil {
					acctName = s
				}
			}
		}

		if filter {
			for _, addr := range addrs {
				_, ok := addresses[addr.EncodeAddress()]
				if ok {
					goto include
				}
			}
			continue
		}

	include:
		// At the moment watch-only addresses are not supported, so all
		// recorded outputs that are not multisig are "spendable".
		// Multisig outputs are only "spendable" if all keys are
		// controlled by this wallet.
		//
		// TODO: Each case will need updates when watch-only addrs
		// is added.  For P2PK, P2PKH, and P2SH, the address must be
		// looked up and not be watching-only.  For multisig, all
		// pubkeys must belong to the manager with the associated
		// private key (currently it only checks whether the pubkey
		// exists, since the private key is required at the moment).
		var spendable bool
	scSwitch:
		switch sc {
		case txscript.PubKeyHashTy:
			spendable = true
		case txscript.PubKeyTy:
			spendable = true
		case txscript.ScriptHashTy:
			spendable = true
		case txscript.MultiSigTy:
			for _, a := range addrs {
				_, err := w.Manager.Address(a)
				if err == nil {
					continue
				}
				if waddrmgr.IsError(err, waddrmgr.ErrAddressNotFound) {
					break scSwitch
				}
				return nil, err
			}
			spendable = true
		}

		result := &btcjson.ListUnspentResult{
			TxID:          output.OutPoint.Hash.String(),
			Vout:          output.OutPoint.Index,
			Account:       acctName,
			ScriptPubKey:  hex.EncodeToString(output.PkScript),
			Amount:        output.Amount.ToBTC(),
			Confirmations: int64(confs),
			Spendable:     spendable,
		}

		// BUG: this should be a JSON array so that all
		// addresses can be included, or removed (and the
		// caller extracts addresses from the pkScript).
		if len(addrs) > 0 {
			result.Address = addrs[0].EncodeAddress()
		}

		results = append(results, result)
	}

	return results, nil
}
示例#24
0
// TestExtractPkScriptAddrs ensures that extracting the type, addresses, and
// number of required signatures from PkScripts works as intended.
func TestExtractPkScriptAddrs(t *testing.T) {
	tests := []struct {
		name    string
		script  []byte
		addrs   []btcutil.Address
		reqSigs int
		class   txscript.ScriptClass
	}{
		{
			name: "standard p2pk with compressed pubkey (0x02)",
			script: decodeHex("2102192d74d0cb94344c9569c2e7790157" +
				"3d8d7903c3ebec3a957724895dca52c6b4ac"),
			addrs: []btcutil.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: []btcutil.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: []btcutil.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: []btcutil.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: []btcutil.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: []btcutil.Address{
				newAddressPubKey(decodeHex("07b0bd634234abbb1" +
					"ba1e986e884185c61cf43e001f9137f23c2c" +
					"409273eb16e6537a576782eba668a7ef8bd3" +
					"b3cfb1edb7117ab65129b8a2e681f3c1e090" +
					"8ef7b")),
			},
			reqSigs: 1,
			class:   txscript.PubKeyTy,
		},
		{
			name: "standard p2pkh",
			script: decodeHex("76a914ad06dd6ddee55cbca9a9e3713bd7" +
				"587509a3056488ac"),
			addrs: []btcutil.Address{
				newAddressPubKeyHash(decodeHex("ad06dd6ddee55" +
					"cbca9a9e3713bd7587509a30564")),
			},
			reqSigs: 1,
			class:   txscript.PubKeyHashTy,
		},
		{
			name: "standard p2sh",
			script: decodeHex("a91463bcc565f9e68ee0189dd5cc67f1b0" +
				"e5f02f45cb87"),
			addrs: []btcutil.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: []btcutil.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: []btcutil.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:   []btcutil.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:   []btcutil.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(
			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
		}
	}
}
示例#25
0
func makeTxSummary(w *Wallet, details *wtxmgr.TxDetails) TransactionSummary {
	serializedTx := details.SerializedTx
	if serializedTx == nil {
		var buf bytes.Buffer
		err := details.MsgTx.Serialize(&buf)
		if err != nil {
			log.Errorf("Transaction serialization: %v", err)
		}
		serializedTx = buf.Bytes()
	}
	var fee btcutil.Amount
	if len(details.Debits) == len(details.MsgTx.TxIn) {
		for _, deb := range details.Debits {
			fee += deb.Amount
		}
		for _, txOut := range details.MsgTx.TxOut {
			fee -= btcutil.Amount(txOut.Value)
		}
	}
	var inputs []TransactionSummaryInput
	if len(details.Debits) != 0 {
		inputs = make([]TransactionSummaryInput, len(details.Debits))
		for i, d := range details.Debits {
			inputs[i] = TransactionSummaryInput{
				Index:           d.Index,
				PreviousAccount: lookupInputAccount(w, details, d),
				PreviousAmount:  d.Amount,
			}
		}
	}
	outputs := make([]TransactionSummaryOutput, 0, len(details.MsgTx.TxOut))
	var credIndex int
	for i, txOut := range details.MsgTx.TxOut {
		mine := len(details.Credits) > credIndex && details.Credits[credIndex].Index == uint32(i)
		output := TransactionSummaryOutput{
			Index:  uint32(i),
			Amount: btcutil.Amount(txOut.Value),
			Mine:   mine,
		}
		if mine {
			acct, internal := lookupOutputChain(w, details, details.Credits[credIndex])
			output.Account = acct
			output.Internal = internal
			credIndex++
		} else {
			_, addresses, _, err := txscript.ExtractPkScriptAddrs(txOut.PkScript, w.chainParams)
			if err == nil {
				output.Addresses = addresses
			}
		}
		outputs = append(outputs, output)
	}
	return TransactionSummary{
		Hash:        &details.Hash,
		Transaction: serializedTx,
		MyInputs:    inputs,
		Outputs:     outputs,
		Fee:         fee,
		Timestamp:   details.Received.Unix(),
	}
}
示例#26
0
// ListTransactions creates a object that may be marshalled to a response result
// for a listtransactions RPC.
//
// TODO: This should be moved out of this package into the main package's
// rpcserver.go, along with everything that requires this.
func ListTransactions(details *wtxmgr.TxDetails, syncHeight int32, net *chaincfg.Params) []btcjson.ListTransactionsResult {
	var (
		blockHashStr  string
		blockTime     int64
		confirmations int64
	)
	if details.Block.Height != -1 {
		blockHashStr = details.Block.Hash.String()
		blockTime = details.Block.Time.Unix()
		confirmations = int64(confirms(details.Block.Height, syncHeight))
	}

	results := []btcjson.ListTransactionsResult{}
	txHashStr := details.Hash.String()
	received := details.Received.Unix()
	generated := blockchain.IsCoinBaseTx(&details.MsgTx)
	recvCat := RecvCategory(details, syncHeight).String()

	send := len(details.Debits) != 0

	// Fee can only be determined if every input is a debit.
	var feeF64 float64
	if len(details.Debits) == len(details.MsgTx.TxIn) {
		var debitTotal btcutil.Amount
		for _, deb := range details.Debits {
			debitTotal += deb.Amount
		}
		var outputTotal btcutil.Amount
		for _, output := range details.MsgTx.TxOut {
			outputTotal += btcutil.Amount(output.Value)
		}
		// Note: The actual fee is debitTotal - outputTotal.  However,
		// this RPC reports negative numbers for fees, so the inverse
		// is calculated.
		feeF64 = (outputTotal - debitTotal).ToBTC()
	}

outputs:
	for i, output := range details.MsgTx.TxOut {
		// Determine if this output is a credit, and if so, determine
		// its spentness.
		var isCredit bool
		var spentCredit bool
		for _, cred := range details.Credits {
			if cred.Index == uint32(i) {
				// Change outputs are ignored.
				if cred.Change {
					continue outputs
				}

				isCredit = true
				spentCredit = cred.Spent
				break
			}
		}

		var address string
		_, addrs, _, _ := txscript.ExtractPkScriptAddrs(output.PkScript, net)
		if len(addrs) == 1 {
			address = addrs[0].EncodeAddress()
		}

		amountF64 := btcutil.Amount(output.Value).ToBTC()
		result := btcjson.ListTransactionsResult{
			// Fields left zeroed:
			//   InvolvesWatchOnly
			//   Account
			//   BlockIndex
			//
			// Fields set below:
			//   Category
			//   Amount
			//   Fee
			Address:         address,
			Vout:            uint32(i),
			Confirmations:   confirmations,
			Generated:       generated,
			BlockHash:       blockHashStr,
			BlockTime:       blockTime,
			TxID:            txHashStr,
			WalletConflicts: []string{},
			Time:            received,
			TimeReceived:    received,
		}

		// Add a received/generated/immature result if this is a credit.
		// If the output was spent, create a second result under the
		// send category with the inverse of the output amount.  It is
		// therefore possible that a single output may be included in
		// the results set zero, one, or two times.
		//
		// Since credits are not saved for outputs that are not
		// controlled by this wallet, all non-credits from transactions
		// with debits are grouped under the send category.

		if send || spentCredit {
			result.Category = "send"
			result.Amount = -amountF64
			result.Fee = &feeF64
			results = append(results, result)
		}
		if isCredit {
			result.Category = recvCat
			result.Amount = amountF64
			result.Fee = nil
			results = append(results, result)
		}
	}
	return results
}
示例#27
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.
	err := w.TxStore.InsertTx(rec, block)
	if err != nil {
		return err
	}

	// 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 {
		_, addrs, _, err := txscript.ExtractPkScriptAddrs(output.PkScript,
			w.chainParams)
		if err != nil {
			// Non-standard outputs are skipped.
			continue
		}
		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
			}
		}
	}

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

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

	return nil
}
示例#28
0
func (s *walletServer) FundTransaction(ctx context.Context, req *pb.FundTransactionRequest) (
	*pb.FundTransactionResponse, error) {

	// TODO: A predicate function for selecting outputs should be created
	// and passed to a database view of just a particular account's utxos to
	// prevent reading every unspent transaction output from every account
	// into memory at once.

	syncBlock := s.wallet.Manager.SyncedTo()

	outputs, err := s.wallet.TxStore.UnspentOutputs()
	if err != nil {
		return nil, translateError(err)
	}

	selectedOutputs := make([]*pb.FundTransactionResponse_PreviousOutput, 0, len(outputs))
	var totalAmount btcutil.Amount
	for i := range outputs {
		output := &outputs[i]

		if !confirmed(req.RequiredConfirmations, output.Height, syncBlock.Height) {
			continue
		}
		target := int32(s.wallet.ChainParams().CoinbaseMaturity)
		if !req.IncludeImmatureCoinbases && output.FromCoinBase &&
			!confirmed(target, output.Height, syncBlock.Height) {
			continue
		}

		_, addrs, _, err := txscript.ExtractPkScriptAddrs(
			output.PkScript, s.wallet.ChainParams())
		if err != nil || len(addrs) == 0 {
			// Cannot determine which account this belongs to
			// without a valid address.  Fix this by saving
			// outputs per account (per-account wtxmgr).
			continue
		}
		outputAcct, err := s.wallet.Manager.AddrAccount(addrs[0])
		if err != nil {
			return nil, translateError(err)
		}
		if outputAcct != req.Account {
			continue
		}

		selectedOutputs = append(selectedOutputs, &pb.FundTransactionResponse_PreviousOutput{
			TransactionHash: output.OutPoint.Hash[:],
			OutputIndex:     output.Index,
			Amount:          int64(output.Amount),
			PkScript:        output.PkScript,
			ReceiveTime:     output.Received.Unix(),
			FromCoinbase:    output.FromCoinBase,
		})
		totalAmount += output.Amount

		if req.TargetAmount != 0 && totalAmount > btcutil.Amount(req.TargetAmount) {
			break
		}

	}

	var changeScript []byte
	if req.IncludeChangeScript && totalAmount > btcutil.Amount(req.TargetAmount) {
		changeAddr, err := s.wallet.NewChangeAddress(req.Account)
		if err != nil {
			return nil, translateError(err)
		}
		changeScript, err = txscript.PayToAddrScript(changeAddr)
		if err != nil {
			return nil, translateError(err)
		}
	}

	return &pb.FundTransactionResponse{
		SelectedOutputs: selectedOutputs,
		TotalAmount:     int64(totalAmount),
		ChangePkScript:  changeScript,
	}, nil
}
示例#29
0
文件: wallet.go 项目: mkl-/lnd
// handleFundingCounterPartyFunds processes the second workflow step for the
// lifetime of a channel reservation. Upon completion, the reservation will
// carry a completed funding transaction (minus the counterparty's input
// signatures), both versions of the commitment transaction, and our signature
// for their version of the commitment transaction.
func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) {
	l.limboMtx.Lock()
	pendingReservation, ok := l.fundingLimbo[req.pendingFundingID]
	l.limboMtx.Unlock()
	if !ok {
		req.err <- fmt.Errorf("attempted to update non-existant funding state")
		return
	}

	// Grab the mutex on the ChannelReservation to ensure thead-safety
	pendingReservation.Lock()
	defer pendingReservation.Unlock()

	// Create a blank, fresh transaction. Soon to be a complete funding
	// transaction which will allow opening a lightning channel.
	pendingReservation.partialState.FundingTx = wire.NewMsgTx()
	fundingTx := pendingReservation.partialState.FundingTx

	// Some temporary variables to cut down on the resolution verbosity.
	pendingReservation.theirContribution = req.contribution
	theirContribution := req.contribution
	ourContribution := pendingReservation.ourContribution

	// First, add all multi-party inputs to the transaction
	// TODO(roasbeef); handle case that tx doesn't exist, fake input
	// TODO(roasbeef): validate SPV proof from other side if in SPV mode.
	//  * actually, pure SPV would need fraud proofs right? must prove input
	//    is unspent
	//  * or, something like getutxo?
	for _, ourInput := range ourContribution.Inputs {
		fundingTx.AddTxIn(ourInput)
	}
	for _, theirInput := range theirContribution.Inputs {
		fundingTx.AddTxIn(theirInput)
	}

	// Next, add all multi-party outputs to the transaction. This includes
	// change outputs for both side.
	for _, ourChangeOutput := range ourContribution.ChangeOutputs {
		fundingTx.AddTxOut(ourChangeOutput)
	}
	for _, theirChangeOutput := range theirContribution.ChangeOutputs {
		fundingTx.AddTxOut(theirChangeOutput)
	}

	ourKey := pendingReservation.partialState.MultiSigKey
	theirKey := theirContribution.MultiSigKey

	// Finally, add the 2-of-2 multi-sig output which will set up the lightning
	// channel.
	channelCapacity := int64(pendingReservation.partialState.Capacity)
	redeemScript, multiSigOut, err := fundMultiSigOut(ourKey.PubKey().SerializeCompressed(),
		theirKey.SerializeCompressed(), channelCapacity)
	if err != nil {
		req.err <- err
		return
	}

	// Register intent for notifications related to the funding output.
	// This'll allow us to properly track the number of confirmations the
	// funding tx has once it has been broadcasted.
	lastBlock := l.Manager.SyncedTo()
	scriptAddr, err := l.Manager.ImportScript(redeemScript, &lastBlock)
	if err != nil {
		req.err <- err
		return
	}
	if err := l.rpc.NotifyReceived([]btcutil.Address{scriptAddr.Address()}); err != nil {
		req.err <- err
		return
	}

	pendingReservation.partialState.FundingRedeemScript = redeemScript
	fundingTx.AddTxOut(multiSigOut)

	// Sort the transaction. Since both side agree to a cannonical
	// ordering, by sorting we no longer need to send the entire
	// transaction. Only signatures will be exchanged.
	txsort.InPlaceSort(pendingReservation.partialState.FundingTx)

	// Next, sign all inputs that are ours, collecting the signatures in
	// order of the inputs.
	pendingReservation.ourFundingSigs = make([][]byte, 0, len(ourContribution.Inputs))
	for i, txIn := range fundingTx.TxIn {
		// Does the wallet know about the txin?
		txDetail, _ := l.TxStore.TxDetails(&txIn.PreviousOutPoint.Hash)
		if txDetail == nil {
			continue
		}

		// Is this our txin? TODO(roasbeef): assumes all inputs are P2PKH...
		prevIndex := txIn.PreviousOutPoint.Index
		prevOut := txDetail.TxRecord.MsgTx.TxOut[prevIndex]
		_, addrs, _, _ := txscript.ExtractPkScriptAddrs(prevOut.PkScript, ActiveNetParams)
		apkh, ok := addrs[0].(*btcutil.AddressPubKeyHash)
		if !ok {
			req.err <- btcwallet.ErrUnsupportedTransactionType
			return
		}

		ai, err := l.Manager.Address(apkh)
		if err != nil {
			req.err <- fmt.Errorf("cannot get address info: %v", err)
			return
		}
		pka := ai.(waddrmgr.ManagedPubKeyAddress)
		privkey, err := pka.PrivKey()
		if err != nil {
			req.err <- fmt.Errorf("cannot get private key: %v", err)
			return
		}

		sigscript, err := txscript.SignatureScript(pendingReservation.partialState.FundingTx, i,
			prevOut.PkScript, txscript.SigHashAll, privkey,
			ai.Compressed())
		if err != nil {
			req.err <- fmt.Errorf("cannot create sigscript: %s", err)
			return
		}

		fundingTx.TxIn[i].SignatureScript = sigscript
		pendingReservation.ourFundingSigs = append(pendingReservation.ourFundingSigs, sigscript)
	}

	// Initialize an empty sha-chain for them, tracking the current pending
	// revocation hash (we don't yet know the pre-image so we can't add it
	// to the chain).
	pendingReservation.partialState.TheirShaChain = shachain.New()
	pendingReservation.partialState.TheirCurrentRevocation = theirContribution.RevocationHash

	// Grab the hash of the current pre-image in our chain, this is needed
	// for our commitment tx.
	// TODO(roasbeef): grab partial state above to avoid long attr chain
	ourCurrentRevokeHash := pendingReservation.ourContribution.RevocationHash

	// Create the txIn to our commitment transaction. In the process, we
	// need to locate the index of the multi-sig output on the funding tx
	// since the outputs are cannonically sorted.
	fundingNTxid := fundingTx.TxSha() // NOTE: assumes testnet-L
	_, multiSigIndex := findScriptOutputIndex(fundingTx, multiSigOut.PkScript)
	fundingTxIn := wire.NewTxIn(wire.NewOutPoint(&fundingNTxid, multiSigIndex), nil)

	// With the funding tx complete, create both commitment transactions.
	initialBalance := ourContribution.FundingAmount
	pendingReservation.fundingLockTime = theirContribution.CsvDelay
	ourCommitKey := ourContribution.CommitKey
	theirCommitKey := theirContribution.CommitKey
	ourCommitTx, err := createCommitTx(fundingTxIn, ourCommitKey, theirCommitKey,
		ourCurrentRevokeHash[:], theirContribution.CsvDelay,
		initialBalance, initialBalance)
	if err != nil {
		req.err <- err
		return
	}
	theirCommitTx, err := createCommitTx(fundingTxIn, theirCommitKey, ourCommitKey,
		theirContribution.RevocationHash[:], theirContribution.CsvDelay,
		initialBalance, initialBalance)
	if err != nil {
		req.err <- err
		return
	}

	// Sort both transactions according to the agreed upon cannonical
	// ordering. This lets us skip sending the entire transaction over,
	// instead we'll just send signatures.
	txsort.InPlaceSort(ourCommitTx)
	txsort.InPlaceSort(theirCommitTx)

	// Record newly available information witin the open channel state.
	pendingReservation.partialState.CsvDelay = theirContribution.CsvDelay
	pendingReservation.partialState.TheirDeliveryAddress = theirContribution.DeliveryAddress
	pendingReservation.partialState.ChanID = fundingNTxid
	pendingReservation.partialState.TheirCommitKey = theirCommitKey
	pendingReservation.partialState.TheirCommitTx = theirCommitTx
	pendingReservation.partialState.OurCommitTx = ourCommitTx

	// Generate a signature for their version of the initial commitment
	// transaction.
	sigTheirCommit, err := txscript.RawTxInSignature(theirCommitTx, 0, redeemScript,
		txscript.SigHashAll, ourKey)
	if err != nil {
		req.err <- err
		return
	}
	pendingReservation.ourCommitmentSig = sigTheirCommit

	req.err <- nil
}
示例#30
0
// testAddrIndexOperations ensures that all normal operations concerning
// the optional address index function correctly.
func testAddrIndexOperations(t *testing.T, db database.Db, newestBlock *btcutil.Block, newestSha *wire.ShaHash, newestBlockIdx int32) {
	// 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 wire.ShaHash
	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 btcutil.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)
	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].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].(*btcutil.AddressPubKey).AddressPubKeyHash().ScriptAddress()
	copy(hash160Bytes[:], testHash160[:])

	// Create a fake index.
	blktxLoc, _ := newestBlock.TxLoc()
	testIndex[hash160Bytes] = []*wire.TxLoc{&blktxLoc[0]}

	// 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.DeleteAddrIndex()
	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.")
	}

}