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:
parent
47c4176b67
commit
a77fb8f0ae
2 changed files with 34 additions and 34 deletions
|
@ -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()
|
||||||
|
|
64
createtx.go
64
createtx.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue