Simplify txToPairs function signature.

This moves the non-error return values for txToPairs into a new
struct, createdTx, and adds an additional value for the payment
address string.
This commit is contained in:
Josh Rickmar 2013-10-28 11:18:37 -04:00
parent 3c390364d7
commit 851a9d6e41
3 changed files with 46 additions and 33 deletions

View file

@ -360,7 +360,7 @@ func SendFrom(reply chan []byte, msg *btcjson.Message) {
pairs := map[string]uint64{ pairs := map[string]uint64{
toaddr58: uint64(amt), toaddr58: uint64(amt),
} }
rawtx, inputs, changeUtxo, err := w.txToPairs(pairs, uint64(fee), int(minconf)) createdTx, err := w.txToPairs(pairs, uint64(fee), int(minconf))
if err != nil { if err != nil {
e := btcjson.ErrInternal e := btcjson.ErrInternal
e.Message = err.Error() e.Message = err.Error()
@ -372,7 +372,7 @@ func SendFrom(reply chan []byte, msg *btcjson.Message) {
n := <-NewJSONID n := <-NewJSONID
var id interface{} = fmt.Sprintf("btcwallet(%v)", n) var id interface{} = fmt.Sprintf("btcwallet(%v)", n)
m, err := btcjson.CreateMessageWithId("sendrawtransaction", id, m, err := btcjson.CreateMessageWithId("sendrawtransaction", id,
hex.EncodeToString(rawtx)) hex.EncodeToString(createdTx.rawTx))
if err != nil { if err != nil {
e := btcjson.ErrInternal e := btcjson.ErrInternal
e.Message = err.Error() e.Message = err.Error()
@ -394,12 +394,12 @@ func SendFrom(reply chan []byte, msg *btcjson.Message) {
// Remove previous unspent outputs now spent by the tx. // Remove previous unspent outputs now spent by the tx.
w.UtxoStore.Lock() w.UtxoStore.Lock()
modified := w.UtxoStore.s.Remove(inputs) modified := w.UtxoStore.s.Remove(createdTx.inputs)
// Add unconfirmed change utxo (if any) to UtxoStore. // Add unconfirmed change utxo (if any) to UtxoStore.
if changeUtxo != nil { if createdTx.changeUtxo != nil {
w.UtxoStore.s = append(w.UtxoStore.s, changeUtxo) w.UtxoStore.s = append(w.UtxoStore.s, createdTx.changeUtxo)
w.ReqSpentUtxoNtfn(changeUtxo) w.ReqSpentUtxoNtfn(createdTx.changeUtxo)
modified = true modified = true
} }
@ -515,7 +515,7 @@ func SendMany(reply chan []byte, msg *btcjson.Message) {
TxFee.Lock() TxFee.Lock()
fee := TxFee.i fee := TxFee.i
TxFee.Unlock() TxFee.Unlock()
rawtx, inputs, changeUtxo, err := w.txToPairs(pairs, uint64(fee), int(minconf)) createdTx, err := w.txToPairs(pairs, uint64(fee), int(minconf))
if err != nil { if err != nil {
e := btcjson.ErrInternal e := btcjson.ErrInternal
e.Message = err.Error() e.Message = err.Error()
@ -527,7 +527,7 @@ func SendMany(reply chan []byte, msg *btcjson.Message) {
n := <-NewJSONID n := <-NewJSONID
var id interface{} = fmt.Sprintf("btcwallet(%v)", n) var id interface{} = fmt.Sprintf("btcwallet(%v)", n)
m, err := btcjson.CreateMessageWithId("sendrawtransaction", id, m, err := btcjson.CreateMessageWithId("sendrawtransaction", id,
hex.EncodeToString(rawtx)) hex.EncodeToString(createdTx.rawTx))
if err != nil { if err != nil {
e := btcjson.ErrInternal e := btcjson.ErrInternal
e.Message = err.Error() e.Message = err.Error()
@ -549,12 +549,12 @@ func SendMany(reply chan []byte, msg *btcjson.Message) {
// Remove previous unspent outputs now spent by the tx. // Remove previous unspent outputs now spent by the tx.
w.UtxoStore.Lock() w.UtxoStore.Lock()
modified := w.UtxoStore.s.Remove(inputs) modified := w.UtxoStore.s.Remove(createdTx.inputs)
// Add unconfirmed change utxo (if any) to UtxoStore. // Add unconfirmed change utxo (if any) to UtxoStore.
if changeUtxo != nil { if createdTx.changeUtxo != nil {
w.UtxoStore.s = append(w.UtxoStore.s, changeUtxo) w.UtxoStore.s = append(w.UtxoStore.s, createdTx.changeUtxo)
w.ReqSpentUtxoNtfn(changeUtxo) w.ReqSpentUtxoNtfn(createdTx.changeUtxo)
modified = true modified = true
} }
@ -577,7 +577,7 @@ func SendMany(reply chan []byte, msg *btcjson.Message) {
// Add hex string of raw tx to sent tx pool. If future blocks // Add hex string of raw tx to sent tx pool. If future blocks
// do not contain a tx, a resend is attempted. // do not contain a tx, a resend is attempted.
UnminedTxs.Lock() UnminedTxs.Lock()
UnminedTxs.m[result.(string)] = hex.EncodeToString(rawtx) UnminedTxs.m[result.(string)] = hex.EncodeToString(createdTx.rawTx)
UnminedTxs.Unlock() UnminedTxs.Unlock()
ReplySuccess(reply, msg.Id, result) ReplySuccess(reply, msg.Id, result)

View file

@ -108,6 +108,16 @@ func selectInputs(s tx.UtxoStore, amt uint64, minconf int) (inputs []*tx.Utxo, b
return inputs, btcout, nil return inputs, btcout, nil
} }
// createdTx is a type holding information regarding a newly-created
// transaction, including the raw bytes, inputs, and a address and UTXO
// for change.
type createdTx struct {
rawTx []byte
inputs []*tx.Utxo
changeAddr string
changeUtxo *tx.Utxo
}
// txToPairs creates a raw transaction sending the amounts for each // txToPairs creates a raw transaction sending the amounts for each
// address/amount pair and fee to each address and the miner. minconf // address/amount pair and fee to each address and the miner. minconf
// specifies the minimum number of confirmations required before an // specifies the minimum number of confirmations required before an
@ -117,7 +127,7 @@ func selectInputs(s tx.UtxoStore, amt uint64, minconf int) (inputs []*tx.Utxo, b
// address, changeUtxo will point to a unconfirmed (height = -1, zeroed // address, changeUtxo will point to a unconfirmed (height = -1, zeroed
// block hash) Utxo. ErrInsufficientFunds is returned if there are not // block hash) Utxo. ErrInsufficientFunds is returned if there are not
// enough eligible unspent outputs to create the transaction. // enough eligible unspent outputs to create the transaction.
func (w *BtcWallet) txToPairs(pairs map[string]uint64, fee uint64, minconf int) (rawtx []byte, inputs []*tx.Utxo, changeUtxo *tx.Utxo, err error) { func (w *BtcWallet) txToPairs(pairs map[string]uint64, fee uint64, minconf int) (*createdTx, 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()
@ -135,20 +145,20 @@ 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.
inputs, btcout, err := selectInputs(w.UtxoStore.s, amt+fee, minconf) inputs, btcout, err := selectInputs(w.UtxoStore.s, amt+fee, minconf)
if err != nil { if err != nil {
return nil, nil, nil, err return 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, nil, nil, fmt.Errorf("cannot decode address: %s", err) return 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, nil, nil, fmt.Errorf("cannot create txout script: %s", err) return 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)
@ -156,23 +166,26 @@ func (w *BtcWallet) txToPairs(pairs map[string]uint64, fee uint64, minconf int)
// Check if there are leftover unspent outputs, and return coins back to // Check if there are leftover unspent outputs, and return coins back to
// a new address we own. // a new address we own.
var changeUtxo *tx.Utxo
var changeAddr string
if btcout > amt+fee { if btcout > amt+fee {
// Create a new address to spend leftover outputs to. // Create a new address to spend leftover outputs to.
// 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() var err error
changeAddr, err = w.NextUnusedAddress()
if err != nil { if err != nil {
return nil, nil, nil, fmt.Errorf("failed to get next unused address: %s", err) return 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) changeAddrHash, _, err := btcutil.DecodeAddress(changeAddr)
if err != nil { if err != nil {
return nil, nil, nil, fmt.Errorf("cannot decode new address: %s", err) return nil, fmt.Errorf("cannot decode new address: %s", err)
} }
pkScript, err := btcscript.PayToPubKeyHashScript(newaddr160) pkScript, err := btcscript.PayToPubKeyHashScript(changeAddrHash)
if err != nil { if err != nil {
return nil, nil, nil, fmt.Errorf("cannot create txout script: %s", err) return nil, fmt.Errorf("cannot create txout script: %s", err)
} }
msgtx.AddTxOut(btcwire.NewTxOut(int64(change), pkScript)) msgtx.AddTxOut(btcwire.NewTxOut(int64(change), pkScript))
@ -187,7 +200,7 @@ func (w *BtcWallet) txToPairs(pairs map[string]uint64, fee uint64, minconf int)
Height: -1, Height: -1,
Subscript: pkScript, Subscript: pkScript,
} }
copy(changeUtxo.AddrHash[:], newaddr160) copy(changeUtxo.AddrHash[:], changeAddrHash)
} }
// Selected unspent outputs become new transaction's inputs. // Selected unspent outputs become new transaction's inputs.
@ -197,11 +210,11 @@ func (w *BtcWallet) txToPairs(pairs map[string]uint64, fee uint64, minconf int)
for i, ip := range inputs { for i, ip := range inputs {
addrstr, err := btcutil.EncodeAddress(ip.AddrHash[:], w.Wallet.Net()) addrstr, err := btcutil.EncodeAddress(ip.AddrHash[:], w.Wallet.Net())
if err != nil { if err != nil {
return nil, nil, nil, err return nil, err
} }
privkey, err := w.GetAddressKey(addrstr) privkey, err := w.GetAddressKey(addrstr)
if err != nil { if err != nil {
return nil, nil, nil, fmt.Errorf("cannot get address key: %v", err) return 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
@ -210,7 +223,7 @@ func (w *BtcWallet) txToPairs(pairs map[string]uint64, fee uint64, minconf int)
sigscript, err := btcscript.SignatureScript(msgtx, i, sigscript, err := btcscript.SignatureScript(msgtx, i,
ip.Subscript, btcscript.SigHashAll, privkey, false) ip.Subscript, btcscript.SigHashAll, privkey, false)
if err != nil { if err != nil {
return nil, nil, nil, fmt.Errorf("cannot create sigscript: %s", err) return nil, fmt.Errorf("cannot create sigscript: %s", err)
} }
msgtx.TxIn[i].SignatureScript = sigscript msgtx.TxIn[i].SignatureScript = sigscript
} }
@ -225,10 +238,10 @@ func (w *BtcWallet) txToPairs(pairs map[string]uint64, fee uint64, minconf int)
engine, err := btcscript.NewScript(txin.SignatureScript, inputs[i].Subscript, i, engine, err := btcscript.NewScript(txin.SignatureScript, inputs[i].Subscript, i,
msgtx, flags) msgtx, flags)
if err != nil { if err != nil {
return nil, nil, nil, fmt.Errorf("cannot create script engine: %s", err) return nil, fmt.Errorf("cannot create script engine: %s", err)
} }
if err = engine.Execute(); err != nil { if err = engine.Execute(); err != nil {
return nil, nil, nil, fmt.Errorf("cannot validate transaction: %s", err) return nil, fmt.Errorf("cannot validate transaction: %s", err)
} }
} }
@ -236,12 +249,12 @@ func (w *BtcWallet) txToPairs(pairs map[string]uint64, fee uint64, minconf int)
if changeUtxo != nil { if changeUtxo != nil {
txHash, err := msgtx.TxSha() txHash, err := msgtx.TxSha()
if err != nil { if err != nil {
return nil, nil, nil, fmt.Errorf("cannot create transaction hash: %s", err) return nil, fmt.Errorf("cannot create transaction hash: %s", err)
} }
copy(changeUtxo.Out.Hash[:], txHash[:]) copy(changeUtxo.Out.Hash[:], txHash[:])
} }
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
msgtx.BtcEncode(buf, btcwire.ProtocolVersion) msgtx.BtcEncode(buf, btcwire.ProtocolVersion)
return buf.Bytes(), inputs, changeUtxo, nil return &createdTx{buf.Bytes(), inputs, changeAddr, changeUtxo}, nil
} }

View file

@ -64,7 +64,7 @@ func TestFakeTxs(t *testing.T) {
pairs := map[string]uint64{ pairs := map[string]uint64{
"17XhEvq9Nahdj7Xe1nv6oRe1tEmaHUuynH": 5000, "17XhEvq9Nahdj7Xe1nv6oRe1tEmaHUuynH": 5000,
} }
rawtx, _, _, err := btcw.txToPairs(pairs, 100, 0) createdTx, err := btcw.txToPairs(pairs, 100, 0)
if err != nil { if err != nil {
t.Errorf("Tx creation failed: %s", err) t.Errorf("Tx creation failed: %s", err)
return return
@ -75,7 +75,7 @@ func TestFakeTxs(t *testing.T) {
Id: "test", Id: "test",
Method: "sendrawtransaction", Method: "sendrawtransaction",
Params: []interface{}{ Params: []interface{}{
hex.EncodeToString(rawtx), hex.EncodeToString(createdTx.rawTx),
}, },
} }
m, _ := json.Marshal(msg) m, _ := json.Marshal(msg)