Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wallet: add GetTransaction returning data for any transaction #779

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 100 additions & 1 deletion wallet/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -2515,14 +2515,21 @@ type GetTransactionResult struct {
BlockHash *chainhash.Hash
Confirmations int32
Timestamp int64
InDb bool
}

// GetTransaction returns detailed data of a transaction given its id. In addition it
// returns properties about its block.
func (w *Wallet) GetTransaction(txHash chainhash.Hash) (*GetTransactionResult,
error) {

var res GetTransactionResult
var (
bestHeight int32
blockHash *chainhash.Hash
summary *TransactionSummary
res GetTransactionResult
)

err := walletdb.View(w.db, func(dbtx walletdb.ReadTx) error {
txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)

Expand All @@ -2536,11 +2543,14 @@ func (w *Wallet) GetTransaction(txHash chainhash.Hash) (*GetTransactionResult,
return fmt.Errorf("%w: txid %v", ErrNoTx, txHash)
}

// Otherwise, we create a summary of the transaction detail.
res = GetTransactionResult{
Summary: makeTxSummary(dbtx, w, txDetail),
Timestamp: txDetail.Block.Time.Unix(),
Confirmations: txDetail.Block.Height,
InDb: true,
}
summary = &res.Summary

// If it is a confirmed transaction we set the corresponding
// block height and hash.
Expand All @@ -2554,6 +2564,95 @@ func (w *Wallet) GetTransaction(txHash chainhash.Hash) (*GetTransactionResult,
if err != nil {
return nil, err
}

// If the transaction was not found in the transaction store, we need to
// build the TransactionSummary from the backend RPC response.
if summary == nil {
chainClient, err := w.requireChainClient()
if err != nil {
return nil, err
}

// Get the transaction data from the backend endpoint.
var txResult *btcjson.TxRawResult
switch client := chainClient.(type) {
case *chain.RPCClient:
txResult, err = client.GetRawTransactionVerbose(&txHash)
if err != nil {
return nil, err
}
case *chain.BitcoindClient:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about Neutrino? Those clients would currently panic on the txResult.Hex access below.

Copy link

@mrfelton mrfelton May 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added this Neutrino check back in, although I originally removed it due to your prior comment!

txResult, err = client.GetRawTransactionVerbose(&txHash)
if err != nil {
return nil, err
}
case *chain.NeutrinoClient:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to include the Neutrino client here, we bailed out earlier for that chain client type.

return nil, errors.New("not supported with " +
"neutrino client")
}

txRaw, err := hex.DecodeString(txResult.Hex)
if err != nil {
return nil, err
}

summary = &TransactionSummary{
Hash: &txHash,
Transaction: txRaw,
MyInputs: nil,
MyOutputs: nil,
Timestamp: txResult.Time,
}

// For a confirmed transaction we must decode the block hash and
// get the block height.
var confs int32
if txResult.BlockHash != "" {
blockHash, err = chainhash.NewHashFromStr(
txResult.BlockHash,
)
if err != nil {
return nil, err
}

// Obtain the block height from the backend.
switch client := chainClient.(type) {
case *chain.RPCClient:
header, err := client.GetBlockHeaderVerbose(
blockHash,
)
if err != nil {
return nil, err
}
bestHeight = header.Height
case *chain.BitcoindClient:
bestHeight, err = client.GetBlockHeight(
blockHash,
)
if err != nil {
return nil, err
}
}

// Determine the number of confirmations.
_, currentHeight, err := chainClient.GetBestBlock()
if err != nil {
return nil, fmt.Errorf("unable to retrieve current "+
"height: %w", err)
}
confs = int32(currentHeight - bestHeight)
}

// Set the TransactionSummary in the GetTransactionResult.
res.Summary = *summary
res.Height = bestHeight
res.BlockHash = summary.Hash
res.Timestamp = summary.Timestamp
res.Confirmations = confs
res.InDb = false
}

// Return the result.
return &res, nil
}

Expand Down