Merge pull request #549 from wpaulino/unify-send-outputs-publish-tx

wallet: unify SendOutputs + PublishTransaction logic
This commit is contained in:
Olaoluwa Osuntokun 2018-09-25 19:15:48 -07:00 committed by GitHub
commit 04340fd158
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -3086,74 +3086,24 @@ func (w *Wallet) TotalReceivedForAddr(addr btcutil.Address, minConf int32) (btcu
func (w *Wallet) SendOutputs(outputs []*wire.TxOut, account uint32, func (w *Wallet) SendOutputs(outputs []*wire.TxOut, account uint32,
minconf int32, satPerKb btcutil.Amount) (*chainhash.Hash, error) { minconf int32, satPerKb btcutil.Amount) (*chainhash.Hash, error) {
chainClient, err := w.requireChainClient() // Ensure the outputs to be created adhere to the network's consensus
if err != nil { // rules.
return nil, err
}
for _, output := range outputs { for _, output := range outputs {
err = txrules.CheckOutput(output, satPerKb) if err := txrules.CheckOutput(output, satPerKb); err != nil {
if err != nil {
return nil, err return nil, err
} }
} }
// Create transaction, replying with an error if the creation // Create the transaction and broadcast it to the network. The
// was not successful. // transaction will be added to the database in order to ensure that we
// continue to re-broadcast the transaction upon restarts until it has
// been confirmed.
createdTx, err := w.CreateSimpleTx(account, outputs, minconf, satPerKb) createdTx, err := w.CreateSimpleTx(account, outputs, minconf, satPerKb)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Create transaction record and insert into the db. return w.publishTransaction(createdTx.Tx)
rec, err := wtxmgr.NewTxRecordFromMsgTx(createdTx.Tx, time.Now())
if err != nil {
log.Errorf("Cannot create record for created transaction: %v", err)
return nil, err
}
err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
txmgrNs := tx.ReadWriteBucket(wtxmgrNamespaceKey)
err := w.TxStore.InsertTx(txmgrNs, rec, nil)
if err != nil {
return err
}
if createdTx.ChangeIndex >= 0 {
err = w.TxStore.AddCredit(txmgrNs, rec, nil, uint32(createdTx.ChangeIndex), true)
if err != nil {
log.Errorf("Error adding change address for sent "+
"tx: %v", err)
return err
}
}
return nil
})
if err != nil {
return nil, err
}
// TODO: The record already has the serialized tx, so no need to
// serialize it again.
txid, err := chainClient.SendRawTransaction(&rec.MsgTx, false)
switch {
case err == nil:
switch w.chainClient.(type) {
// For neutrino we need to trigger adding relevant tx manually
// because for spv client - tx data isn't received from sync peer.
case *chain.NeutrinoClient:
err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
return w.addRelevantTx(tx, rec, nil)
})
if err != nil {
return nil, err
}
}
// TODO(roasbeef): properly act on rest of mapped errors
return txid, nil
default:
return nil, err
}
} }
// SignatureError records the underlying error when validating a transaction // SignatureError records the underlying error when validating a transaction
@ -3299,9 +3249,14 @@ func (w *Wallet) SignTransaction(tx *wire.MsgTx, hashType txscript.SigHashType,
// This function is unstable and will be removed once syncing code is moved out // This function is unstable and will be removed once syncing code is moved out
// of the wallet. // of the wallet.
func (w *Wallet) PublishTransaction(tx *wire.MsgTx) error { func (w *Wallet) PublishTransaction(tx *wire.MsgTx) error {
_, err := w.publishTransaction(tx)
return err
}
func (w *Wallet) publishTransaction(tx *wire.MsgTx) (*chainhash.Hash, error) {
server, err := w.requireChainClient() server, err := w.requireChainClient()
if err != nil { if err != nil {
return err return nil, err
} }
// As we aim for this to be general reliable transaction broadcast API, // As we aim for this to be general reliable transaction broadcast API,
@ -3310,29 +3265,19 @@ func (w *Wallet) PublishTransaction(tx *wire.MsgTx) error {
// set of records. // set of records.
txRec, err := wtxmgr.NewTxRecordFromMsgTx(tx, time.Now()) txRec, err := wtxmgr.NewTxRecordFromMsgTx(tx, time.Now())
if err != nil { if err != nil {
return err return nil, err
} }
err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error { err = walletdb.Update(w.db, func(dbTx walletdb.ReadWriteTx) error {
txmgrNs := tx.ReadWriteBucket(wtxmgrNamespaceKey) return w.addRelevantTx(dbTx, txRec, nil)
return w.TxStore.InsertTx(txmgrNs, txRec, nil)
}) })
if err != nil { if err != nil {
return err return nil, err
} }
_, err = server.SendRawTransaction(tx, false) txid, err := server.SendRawTransaction(tx, false)
switch { switch {
case err == nil: case err == nil:
switch w.chainClient.(type) { return txid, nil
// For neutrino we need to trigger adding relevant tx manually
// because for spv client - tx data isn't received from sync peer.
case *chain.NeutrinoClient:
return walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
return w.addRelevantTx(tx, txRec, nil)
})
}
return nil
// The following are errors returned from btcd's mempool. // The following are errors returned from btcd's mempool.
case strings.Contains(err.Error(), "spent"): case strings.Contains(err.Error(), "spent"):
@ -3348,25 +3293,23 @@ func (w *Wallet) PublishTransaction(tx *wire.MsgTx) error {
case strings.Contains(err.Error(), "Missing inputs"): case strings.Contains(err.Error(), "Missing inputs"):
fallthrough fallthrough
case strings.Contains(err.Error(), "already in block chain"): case strings.Contains(err.Error(), "already in block chain"):
// If the transaction was rejected, then we'll remove it from // If the transaction was rejected, then we'll remove it from
// the txstore, as otherwise, we'll attempt to continually // the txstore, as otherwise, we'll attempt to continually
// re-broadcast it, and the utxo state of the wallet won't be // re-broadcast it, and the utxo state of the wallet won't be
// accurate. // accurate.
dbErr := walletdb.Update(w.db, func(dbTx walletdb.ReadWriteTx) error { dbErr := walletdb.Update(w.db, func(dbTx walletdb.ReadWriteTx) error {
txmgrNs := dbTx.ReadWriteBucket(wtxmgrNamespaceKey) txmgrNs := dbTx.ReadWriteBucket(wtxmgrNamespaceKey)
return w.TxStore.RemoveUnminedTx(txmgrNs, txRec) return w.TxStore.RemoveUnminedTx(txmgrNs, txRec)
}) })
if dbErr != nil { if dbErr != nil {
return fmt.Errorf("unable to broadcast tx: %v, "+ return nil, fmt.Errorf("unable to broadcast tx: %v, "+
"unable to remove invalid tx: %v", err, dbErr) "unable to remove invalid tx: %v", err, dbErr)
} }
return err return nil, err
default: default:
return err return nil, err
} }
} }