Skip to content

Commit eae108d

Browse files
committed
feat: Add block_height to UTXOSpendReport and test JSON serialization
1 parent 421b509 commit eae108d

3 files changed

Lines changed: 85 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Add block_height to UTXOSpendReport for more efficient scans.
13+
1014
## [0.6.1] - 2026-01-09
1115

1216
### Fixed

neutrino_server/internal/neutrino/node.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ type UTXOSpendReport struct {
374374
Unspent bool `json:"unspent"`
375375
Value int64 `json:"value,omitempty"`
376376
ScriptPubKey string `json:"scriptpubkey,omitempty"`
377+
BlockHeight uint32 `json:"block_height,omitempty"`
377378

378379
// If the output has been spent, these fields are populated
379380
SpendingTxID string `json:"spending_txid,omitempty"`
@@ -524,6 +525,7 @@ func (n *Node) GetUTXO(txid string, vout uint32, address string, startHeight int
524525
txOut := foundTx.TxOut[vout]
525526
report.Value = txOut.Value
526527
report.ScriptPubKey = fmt.Sprintf("%x", txOut.PkScript)
528+
report.BlockHeight = uint32(foundHeight)
527529
} else {
528530
// UTXO has been spent
529531
report.Unspent = false

neutrino_server/internal/neutrino/node_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package neutrino
22

33
import (
4+
"encoding/json"
45
"os"
56
"testing"
67
"time"
@@ -196,3 +197,81 @@ func TestGetStatus(t *testing.T) {
196197
t.Errorf("expected 0 peers, got %d", status.Peers)
197198
}
198199
}
200+
201+
func TestUTXOSpendReportJSON(t *testing.T) {
202+
tests := []struct {
203+
name string
204+
report UTXOSpendReport
205+
wantKeys []string // JSON keys that must be present
206+
noKeys []string // JSON keys that must NOT be present (omitempty)
207+
}{
208+
{
209+
name: "unspent with block_height",
210+
report: UTXOSpendReport{
211+
Unspent: true,
212+
Value: 100000,
213+
ScriptPubKey: "00140000000000000000000000000000000000000000",
214+
BlockHeight: 295000,
215+
},
216+
wantKeys: []string{"unspent", "value", "scriptpubkey", "block_height"},
217+
noKeys: []string{"spending_txid", "spending_input", "spending_height"},
218+
},
219+
{
220+
name: "unspent without block_height (omitempty)",
221+
report: UTXOSpendReport{
222+
Unspent: true,
223+
Value: 50000,
224+
ScriptPubKey: "00140000000000000000000000000000000000000000",
225+
BlockHeight: 0,
226+
},
227+
wantKeys: []string{"unspent", "value", "scriptpubkey"},
228+
noKeys: []string{"block_height"},
229+
},
230+
{
231+
name: "spent UTXO",
232+
report: UTXOSpendReport{
233+
Unspent: false,
234+
SpendingTxID: "abcd1234",
235+
SpendingInput: 0,
236+
SpendingHeight: 295001,
237+
},
238+
wantKeys: []string{"unspent", "spending_txid", "spending_height"},
239+
noKeys: []string{"value", "scriptpubkey", "block_height"},
240+
},
241+
}
242+
243+
for _, tt := range tests {
244+
t.Run(tt.name, func(t *testing.T) {
245+
data, err := json.Marshal(tt.report)
246+
if err != nil {
247+
t.Fatalf("json.Marshal() error: %v", err)
248+
}
249+
250+
var parsed map[string]interface{}
251+
if err := json.Unmarshal(data, &parsed); err != nil {
252+
t.Fatalf("json.Unmarshal() error: %v", err)
253+
}
254+
255+
for _, key := range tt.wantKeys {
256+
if _, ok := parsed[key]; !ok {
257+
t.Errorf("expected key %q in JSON, got: %s", key, string(data))
258+
}
259+
}
260+
261+
for _, key := range tt.noKeys {
262+
if _, ok := parsed[key]; ok {
263+
t.Errorf("unexpected key %q in JSON (omitempty), got: %s", key, string(data))
264+
}
265+
}
266+
267+
// Verify block_height value when present
268+
if tt.report.BlockHeight > 0 {
269+
if bh, ok := parsed["block_height"].(float64); !ok {
270+
t.Errorf("block_height is not a number: %v", parsed["block_height"])
271+
} else if uint32(bh) != tt.report.BlockHeight {
272+
t.Errorf("block_height = %v, want %d", bh, tt.report.BlockHeight)
273+
}
274+
}
275+
})
276+
}
277+
}

0 commit comments

Comments
 (0)