Skip to content

Commit a573d52

Browse files
committed
Implement GetTransaction function
The function queries electrum for a details of the specific transaction and converts it in a format expected by the bitcoin.Chain interface. The simpliest solution is to use `GetTransaction` function to query electrum as it returns the transaction details in verbose format. Unfortunatelly it's not compatible with Esplora/Electrs implementation of ElectrumX server. (see: Blockstream/electrs#36) As a workaround to cover integration with Esplora/Electrs we use `GetRawTransaction` and deserialize the transaction.
1 parent 8b5b5b3 commit a573d52

File tree

2 files changed

+96
-2
lines changed

2 files changed

+96
-2
lines changed

pkg/bitcoin/electrum/electrum.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,38 @@ func Connect(ctx context.Context, config Config) (bitcoin.Chain, error) {
116116
}, nil
117117
}
118118

119+
// GetTransaction gets the transaction with the given transaction hash.
120+
// If the transaction with the given hash was not found on the chain,
121+
// this function returns an error.
119122
func (c *Connection) GetTransaction(
120123
transactionHash bitcoin.Hash,
121124
) (*bitcoin.Transaction, error) {
122-
// TODO: Implementation.
123-
panic("not implemented")
125+
txID := transactionHash.Hex(bitcoin.ReversedByteOrder)
126+
127+
var rawTransaction string
128+
err := wrappers.DoWithDefaultRetry(c.requestRetryTimeout, func(ctx context.Context) error {
129+
// We cannot use `GetTransaction` to get the the transaction details
130+
// as Esplora/Electrs doesn't support verbose transactions.
131+
// See: https://github.com/Blockstream/electrs/pull/36
132+
rawTx, err := c.client.GetRawTransaction(c.ctx, txID)
133+
if err != nil {
134+
return fmt.Errorf("GetRawTransaction failed: [%w]", err)
135+
}
136+
137+
rawTransaction = rawTx
138+
139+
return nil
140+
})
141+
if err != nil {
142+
return nil, fmt.Errorf("failed to get raw transaction [%s]: [%w]", txID, err)
143+
}
144+
145+
result, err := convertRawTransaction(rawTransaction)
146+
if err != nil {
147+
return nil, fmt.Errorf("failed to convert transaction: [%w]", err)
148+
}
149+
150+
return result, nil
124151
}
125152

126153
func (c *Connection) GetTransactionConfirmations(

pkg/bitcoin/electrum/transaction.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package electrum
2+
3+
import (
4+
"bytes"
5+
"encoding/hex"
6+
"fmt"
7+
8+
"github.com/btcsuite/btcd/wire"
9+
10+
"github.com/keep-network/keep-core/pkg/bitcoin"
11+
)
12+
13+
// decodeTransaction deserializes a transaction from the hexadecimal serialized
14+
// string to a btcd message format.
15+
func decodeTransaction(rawTx string) (*wire.MsgTx, error) {
16+
headerBytes, err := hex.DecodeString(rawTx)
17+
if err != nil {
18+
return nil, fmt.Errorf("failed to decode a hex string: [%w]", err)
19+
}
20+
21+
buf := bytes.NewBuffer(headerBytes)
22+
23+
var t wire.MsgTx
24+
if err := t.Deserialize(buf); err != nil {
25+
return nil, fmt.Errorf("failed to deserialize a transaction: [%w]", err)
26+
}
27+
28+
return &t, nil
29+
}
30+
31+
// convertRawTransaction transforms a transaction provided in the hexadecimal serialized
32+
// string to the format expected by the bitcoin.Chain interface.
33+
func convertRawTransaction(rawTx string) (*bitcoin.Transaction, error) {
34+
t, err := decodeTransaction(rawTx)
35+
if err != nil {
36+
return nil, fmt.Errorf("failed to decode a transaction: [%w]", err)
37+
}
38+
39+
result := &bitcoin.Transaction{
40+
Version: int32(t.Version),
41+
Locktime: t.LockTime,
42+
}
43+
44+
for _, vin := range t.TxIn {
45+
input := &bitcoin.TransactionInput{
46+
Outpoint: &bitcoin.TransactionOutpoint{
47+
TransactionHash: bitcoin.Hash(vin.PreviousOutPoint.Hash),
48+
OutputIndex: vin.PreviousOutPoint.Index,
49+
},
50+
SignatureScript: vin.SignatureScript,
51+
Sequence: vin.Sequence,
52+
}
53+
54+
result.Inputs = append(result.Inputs, input)
55+
}
56+
57+
for _, vout := range t.TxOut {
58+
output := &bitcoin.TransactionOutput{
59+
Value: vout.Value,
60+
PublicKeyScript: vout.PkScript,
61+
}
62+
63+
result.Outputs = append(result.Outputs, output)
64+
}
65+
66+
return result, nil
67+
}

0 commit comments

Comments
 (0)