Merge pull request #549 from wpaulino/unify-send-outputs-publish-tx
wallet: unify SendOutputs + PublishTransaction logic
This commit is contained in:
commit
04340fd158
1 changed files with 23 additions and 80 deletions
103
wallet/wallet.go
103
wallet/wallet.go
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue