Avoid calling getrawtransaction from signrawtransaction.

Replace all calls to getrawtransaction with gettxout.

As btcd no longer enables the transaction index by default,
getrawtransaction can no longer be depended on.  gettxout is safe to
use since it queries the utxo set.  This also means that it will
continue to work even when pruning is enabled.

This should be further improved in the future to not look up previous
output scripts over btcd rpc when they are already saved by the
wallet.

Fixes #410.
This commit is contained in:
Josh Rickmar 2016-04-22 11:50:12 -04:00
parent c82b8bae20
commit a549b6908a

View file

@ -1622,13 +1622,6 @@ func SignMessage(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
return base64.StdEncoding.EncodeToString(sigbytes), nil return base64.StdEncoding.EncodeToString(sigbytes), nil
} }
// pendingTx is used for async fetching of transaction dependancies in
// SignRawTransaction.
type pendingTx struct {
resp btcrpcclient.FutureGetRawTransactionResult
inputs []uint32 // list of inputs that care about this tx.
}
// SignRawTransaction handles the signrawtransaction command. // SignRawTransaction handles the signrawtransaction command.
func SignRawTransaction(icmd interface{}, w *wallet.Wallet, chainClient *chain.RPCClient) (interface{}, error) { func SignRawTransaction(icmd interface{}, w *wallet.Wallet, chainClient *chain.RPCClient) (interface{}, error) {
cmd := icmd.(*btcjson.SignRawTransactionCmd) cmd := icmd.(*btcjson.SignRawTransactionCmd)
@ -1711,30 +1704,17 @@ func SignRawTransaction(icmd interface{}, w *wallet.Wallet, chainClient *chain.R
// querying btcd with getrawtransaction. We queue up a bunch of async // querying btcd with getrawtransaction. We queue up a bunch of async
// requests and will wait for replies after we have checked the rest of // requests and will wait for replies after we have checked the rest of
// the arguments. // the arguments.
requested := make(map[wire.ShaHash]*pendingTx) requested := make(map[wire.OutPoint]btcrpcclient.FutureGetTxOutResult)
for _, txIn := range tx.TxIn { for _, txIn := range tx.TxIn {
// Did we get this txin from the arguments? // Did we get this outpoint from the arguments?
if _, ok := inputs[txIn.PreviousOutPoint]; ok { if _, ok := inputs[txIn.PreviousOutPoint]; ok {
continue continue
} }
// Are we already fetching this tx? If so mark us as interested // Asynchronously request the output script.
// in this outpoint. (N.B. that any *sane* tx will only requested[txIn.PreviousOutPoint] = chainClient.GetTxOutAsync(
// reference each outpoint once, since anything else is a double &txIn.PreviousOutPoint.Hash, txIn.PreviousOutPoint.Index,
// spend. We don't check this ourselves to save having to scan true)
// the array, it will fail later if so).
if ptx, ok := requested[txIn.PreviousOutPoint.Hash]; ok {
ptx.inputs = append(ptx.inputs,
txIn.PreviousOutPoint.Index)
continue
}
// Never heard of this one before, request it.
prevHash := &txIn.PreviousOutPoint.Hash
requested[txIn.PreviousOutPoint.Hash] = &pendingTx{
resp: chainClient.GetRawTransactionAsync(prevHash),
inputs: []uint32{txIn.PreviousOutPoint.Index},
}
} }
// Parse list of private keys, if present. If there are any keys here // Parse list of private keys, if present. If there are any keys here
@ -1767,24 +1747,16 @@ func SignRawTransaction(icmd interface{}, w *wallet.Wallet, chainClient *chain.R
// We have checked the rest of the args. now we can collect the async // We have checked the rest of the args. now we can collect the async
// txs. TODO: If we don't mind the possibility of wasting work we could // txs. TODO: If we don't mind the possibility of wasting work we could
// move waiting to the following loop and be slightly more asynchronous. // move waiting to the following loop and be slightly more asynchronous.
for txid, ptx := range requested { for outPoint, resp := range requested {
tx, err := ptx.resp.Receive() result, err := resp.Receive()
if err != nil { if err != nil {
return nil, err return nil, err
} }
script, err := hex.DecodeString(result.ScriptPubKey.Hex)
for _, input := range ptx.inputs { if err != nil {
if input >= uint32(len(tx.MsgTx().TxOut)) { return nil, err
e := fmt.Errorf("input %s:%d is not in tx",
txid.String(), input)
return nil, InvalidParameterError{e}
}
inputs[wire.OutPoint{
Hash: txid,
Index: input,
}] = tx.MsgTx().TxOut[input].PkScript
} }
inputs[outPoint] = script
} }
// All args collected. Now we can sign all the inputs that we can. // All args collected. Now we can sign all the inputs that we can.