Return used transaction inputs from txToPairs.

These inputs will need to be removed from the stored utxos if the
transaction is sent and accepted in a block.
This commit is contained in:
Josh Rickmar 2013-10-14 16:14:04 -04:00
parent 47c4176b67
commit a77fb8f0ae
2 changed files with 34 additions and 34 deletions

View file

@ -475,7 +475,7 @@ func SendFrom(reply chan []byte, msg *btcjson.Message) {
pairs := map[string]uint64{ pairs := map[string]uint64{
toaddr58: uint64(amt), toaddr58: uint64(amt),
} }
rawtx, err := w.txToPairs(pairs, uint64(fee), int(minconf)) rawtx, _, err := w.txToPairs(pairs, uint64(fee), int(minconf))
if err != nil { if err != nil {
e := InternalError e := InternalError
e.Message = err.Error() e.Message = err.Error()
@ -592,7 +592,7 @@ func SendMany(reply chan []byte, msg *btcjson.Message) {
TxFee.Lock() TxFee.Lock()
fee := TxFee.i fee := TxFee.i
TxFee.Unlock() TxFee.Unlock()
rawtx, err := w.txToPairs(pairs, uint64(fee), int(minconf)) rawtx, _, err := w.txToPairs(pairs, uint64(fee), int(minconf))
if err != nil { if err != nil {
e := InternalError e := InternalError
e.Message = err.Error() e.Message = err.Error()

View file

@ -60,18 +60,18 @@ func (u ByAmount) Swap(i, j int) {
u[i], u[j] = u[j], u[i] u[i], u[j] = u[j], u[i]
} }
// selectOutputs selects the minimum number possible of unspent // selectInputs selects the minimum number possible of unspent
// outputs to use to create a new transaction that spends amt satoshis. // outputs to use to create a new transaction that spends amt satoshis.
// Outputs with less than minconf confirmations are ignored. btcout is // Previous outputs with less than minconf confirmations are ignored. btcout
// the total number of satoshis which would be spent by the combination // is the total number of satoshis which would be spent by the combination
// of all selected outputs. err will equal ErrInsufficientFunds if there // of all selected previous outputs. err will equal ErrInsufficientFunds if there
// are not enough unspent outputs to spend amt. // are not enough unspent outputs to spend amt.
func selectOutputs(s tx.UtxoStore, amt uint64, minconf int) (outputs []*tx.Utxo, btcout uint64, err error) { func selectInputs(s tx.UtxoStore, amt uint64, minconf int) (inputs []*tx.Utxo, btcout uint64, err error) {
height := getCurHeight() height := getCurHeight()
// Create list of eligible unspent outputs to use as tx inputs, and // Create list of eligible unspent previous outputs to use as tx
// sort by the amount in reverse order so a minimum number of // inputs, and sort by the amount in reverse order so a minimum number
// inputs is needed. // of inputs is needed.
var eligible []*tx.Utxo var eligible []*tx.Utxo
for _, utxo := range s { for _, utxo := range s {
if int(height-utxo.Height) >= minconf { if int(height-utxo.Height) >= minconf {
@ -80,20 +80,20 @@ func selectOutputs(s tx.UtxoStore, amt uint64, minconf int) (outputs []*tx.Utxo,
} }
sort.Sort(sort.Reverse(ByAmount(eligible))) sort.Sort(sort.Reverse(ByAmount(eligible)))
// Iterate throguh eligible transactions, appending to coins and // Iterate throguh eligible transactions, appending to outputs and
// increasing btcout. This is finished when btcout is greater than the // increasing btcout. This is finished when btcout is greater than the
// requested amt to spend. // requested amt to spend.
for _, u := range eligible { for _, u := range eligible {
outputs = append(outputs, u) inputs = append(inputs, u)
if btcout += u.Amt; btcout >= amt { if btcout += u.Amt; btcout >= amt {
return outputs, btcout, nil return inputs, btcout, nil
} }
} }
if btcout < amt { if btcout < amt {
return nil, 0, ErrInsufficientFunds return nil, 0, ErrInsufficientFunds
} }
return outputs, btcout, nil return inputs, btcout, nil
} }
// txToPairs creates a raw transaction sending the amounts for each // txToPairs creates a raw transaction sending the amounts for each
@ -103,7 +103,7 @@ func selectOutputs(s tx.UtxoStore, amt uint64, minconf int) (outputs []*tx.Utxo,
// to addr or as a fee for the miner are sent to a newly generated // to addr or as a fee for the miner are sent to a newly generated
// address. ErrInsufficientFunds is returned if there are not enough // address. ErrInsufficientFunds is returned if there are not enough
// eligible unspent outputs to create the transaction. // eligible unspent outputs to create the transaction.
func (w *BtcWallet) txToPairs(pairs map[string]uint64, fee uint64, minconf int) (rawtx []byte, err error) { func (w *BtcWallet) txToPairs(pairs map[string]uint64, fee uint64, minconf int) (rawtx []byte, inputs []*tx.Utxo, err error) {
// Recorded unspent transactions should not be modified until this // Recorded unspent transactions should not be modified until this
// finishes. // finishes.
w.UtxoStore.RLock() w.UtxoStore.RLock()
@ -119,22 +119,22 @@ func (w *BtcWallet) txToPairs(pairs map[string]uint64, fee uint64, minconf int)
} }
// Select unspent outputs to be used in transaction. // Select unspent outputs to be used in transaction.
outputs, btcout, err := selectOutputs(w.UtxoStore.s, amt+fee, minconf) inputs, btcout, err := selectInputs(w.UtxoStore.s, amt+fee, minconf)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
// Add outputs to new tx. // Add outputs to new tx.
for addr, amt := range pairs { for addr, amt := range pairs {
addr160, _, err := btcutil.DecodeAddress(addr) addr160, _, err := btcutil.DecodeAddress(addr)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot decode address: %s", err) return nil, nil, fmt.Errorf("cannot decode address: %s", err)
} }
// Spend amt to addr160 // Spend amt to addr160
pkScript, err := btcscript.PayToPubKeyHashScript(addr160) pkScript, err := btcscript.PayToPubKeyHashScript(addr160)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot create txout script: %s", err) return nil, nil, fmt.Errorf("cannot create txout script: %s", err)
} }
txout := btcwire.NewTxOut(int64(amt), pkScript) txout := btcwire.NewTxOut(int64(amt), pkScript)
msgtx.AddTxOut(txout) msgtx.AddTxOut(txout)
@ -147,43 +147,43 @@ func (w *BtcWallet) txToPairs(pairs map[string]uint64, fee uint64, minconf int)
// TODO(jrick): use the next chained address, not the next unused. // TODO(jrick): use the next chained address, not the next unused.
newaddr, err := w.NextUnusedAddress() newaddr, err := w.NextUnusedAddress()
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get next unused address: %s", err) return nil, nil, fmt.Errorf("failed to get next unused address: %s", err)
} }
// Spend change // Spend change
change := btcout - (amt + fee) change := btcout - (amt + fee)
newaddr160, _, err := btcutil.DecodeAddress(newaddr) newaddr160, _, err := btcutil.DecodeAddress(newaddr)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot decode new address: %s", err) return nil, nil, fmt.Errorf("cannot decode new address: %s", err)
} }
pkScript, err := btcscript.PayToPubKeyHashScript(newaddr160) pkScript, err := btcscript.PayToPubKeyHashScript(newaddr160)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot create txout script: %s", err) return nil, nil, fmt.Errorf("cannot create txout script: %s", err)
} }
msgtx.AddTxOut(btcwire.NewTxOut(int64(change), pkScript)) msgtx.AddTxOut(btcwire.NewTxOut(int64(change), pkScript))
} }
// Selected unspent outputs become new transaction's inputs. // Selected unspent outputs become new transaction's inputs.
for _, op := range outputs { for _, ip := range inputs {
msgtx.AddTxIn(btcwire.NewTxIn((*btcwire.OutPoint)(&op.Out), nil)) msgtx.AddTxIn(btcwire.NewTxIn((*btcwire.OutPoint)(&ip.Out), nil))
} }
for i, op := range outputs { for i, ip := range inputs {
addrstr, err := btcutil.EncodeAddress(op.AddrHash[:], w.Wallet.Net()) addrstr, err := btcutil.EncodeAddress(ip.AddrHash[:], w.Wallet.Net())
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
privkey, err := w.GetAddressKey(addrstr) privkey, err := w.GetAddressKey(addrstr)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot get address key: %v", err) return nil, nil, fmt.Errorf("cannot get address key: %v", err)
} }
// TODO(jrick): we want compressed pubkeys. Switch wallet to // TODO(jrick): we want compressed pubkeys. Switch wallet to
// generate addresses from the compressed key. This will break // generate addresses from the compressed key. This will break
// armory wallet compat but oh well. // armory wallet compat but oh well.
sigscript, err := btcscript.SignatureScript(msgtx, i, sigscript, err := btcscript.SignatureScript(msgtx, i,
op.Subscript, btcscript.SigHashAll, privkey, false) ip.Subscript, btcscript.SigHashAll, privkey, false)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot create sigscript: %s", err) return nil, nil, fmt.Errorf("cannot create sigscript: %s", err)
} }
msgtx.TxIn[i].SignatureScript = sigscript msgtx.TxIn[i].SignatureScript = sigscript
} }
@ -191,17 +191,17 @@ func (w *BtcWallet) txToPairs(pairs map[string]uint64, fee uint64, minconf int)
// Validate msgtx before returning the raw transaction. // Validate msgtx before returning the raw transaction.
bip16 := time.Now().After(btcscript.Bip16Activation) bip16 := time.Now().After(btcscript.Bip16Activation)
for i, txin := range msgtx.TxIn { for i, txin := range msgtx.TxIn {
engine, err := btcscript.NewScript(txin.SignatureScript, outputs[i].Subscript, i, engine, err := btcscript.NewScript(txin.SignatureScript, inputs[i].Subscript, i,
msgtx, bip16) msgtx, bip16)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot create script engine: %s", err) return nil, nil, fmt.Errorf("cannot create script engine: %s", err)
} }
if err = engine.Execute(); err != nil { if err = engine.Execute(); err != nil {
return nil, fmt.Errorf("cannot validate transaction: %s", err) return nil, nil, fmt.Errorf("cannot validate transaction: %s", err)
} }
} }
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
msgtx.BtcEncode(buf, btcwire.ProtocolVersion) msgtx.BtcEncode(buf, btcwire.ProtocolVersion)
return buf.Bytes(), nil return buf.Bytes(), inputs, nil
} }