From 97123d51a4847b276de97e7aff79b791f39ff4c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Olav?= Date: Sat, 8 Nov 2025 15:23:14 +0100 Subject: [PATCH] importdescriptors: create custom command, pass on active --- gen/bitcoin/bitcoind/v1alpha/bitcoin.pb.go | 17 +++++++-- proto/bitcoin/bitcoind/v1alpha/bitcoin.proto | 2 + server/commands/bitcoind.go | 16 ++++++++ server/server.go | 40 +++++++++++--------- 4 files changed, 54 insertions(+), 21 deletions(-) diff --git a/gen/bitcoin/bitcoind/v1alpha/bitcoin.pb.go b/gen/bitcoin/bitcoind/v1alpha/bitcoin.pb.go index fdd04e1..f9109c4 100644 --- a/gen/bitcoin/bitcoind/v1alpha/bitcoin.pb.go +++ b/gen/bitcoin/bitcoind/v1alpha/bitcoin.pb.go @@ -6790,6 +6790,8 @@ type ImportDescriptorsRequest_Request struct { Active bool `protobuf:"varint,2,opt,name=active,proto3" json:"active,omitempty"` RangeStart uint32 `protobuf:"varint,3,opt,name=range_start,json=rangeStart,proto3" json:"range_start,omitempty"` RangeEnd uint32 `protobuf:"varint,4,opt,name=range_end,json=rangeEnd,proto3" json:"range_end,omitempty"` + // If a ranged descriptor is set to active, this specifies the next index to generate addresses from + NextIndex uint32 `protobuf:"varint,8,opt,name=next_index,json=nextIndex,proto3" json:"next_index,omitempty"` // Nil passes 'now' to Bitcoin Core, which bypasses scanning. Timestamp *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // Whether matching outputs should be treated as not incoming payments (e.g. change) @@ -6858,6 +6860,13 @@ func (x *ImportDescriptorsRequest_Request) GetRangeEnd() uint32 { return 0 } +func (x *ImportDescriptorsRequest_Request) GetNextIndex() uint32 { + if x != nil { + return x.NextIndex + } + return 0 +} + func (x *ImportDescriptorsRequest_Request) GetTimestamp() *timestamppb.Timestamp { if x != nil { return x.Timestamp @@ -8088,10 +8097,10 @@ const file_bitcoin_bitcoind_v1alpha_bitcoin_proto_rawDesc = "" + "\aversion\x18\x06 \x01(\rR\aversion\x12\x1a\n" + "\blocktime\x18\a \x01(\rR\blocktime\x127\n" + "\x06inputs\x18\b \x03(\v2\x1f.bitcoin.bitcoind.v1alpha.InputR\x06inputs\x12:\n" + - "\aoutputs\x18\t \x03(\v2 .bitcoin.bitcoind.v1alpha.OutputR\aoutputs\"\xf8\x02\n" + + "\aoutputs\x18\t \x03(\v2 .bitcoin.bitcoind.v1alpha.OutputR\aoutputs\"\x97\x03\n" + "\x18ImportDescriptorsRequest\x12\x16\n" + "\x06wallet\x18\x01 \x01(\tR\x06wallet\x12V\n" + - "\brequests\x18\x02 \x03(\v2:.bitcoin.bitcoind.v1alpha.ImportDescriptorsRequest.RequestR\brequests\x1a\xeb\x01\n" + + "\brequests\x18\x02 \x03(\v2:.bitcoin.bitcoind.v1alpha.ImportDescriptorsRequest.RequestR\brequests\x1a\x8a\x02\n" + "\aRequest\x12\x1e\n" + "\n" + "descriptor\x18\x01 \x01(\tR\n" + @@ -8099,7 +8108,9 @@ const file_bitcoin_bitcoind_v1alpha_bitcoin_proto_rawDesc = "" + "\x06active\x18\x02 \x01(\bR\x06active\x12\x1f\n" + "\vrange_start\x18\x03 \x01(\rR\n" + "rangeStart\x12\x1b\n" + - "\trange_end\x18\x04 \x01(\rR\brangeEnd\x128\n" + + "\trange_end\x18\x04 \x01(\rR\brangeEnd\x12\x1d\n" + + "\n" + + "next_index\x18\b \x01(\rR\tnextIndex\x128\n" + "\ttimestamp\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\ttimestamp\x12\x1a\n" + "\binternal\x18\x06 \x01(\bR\binternal\x12\x14\n" + "\x05label\x18\a \x01(\tR\x05label\"\xc2\x02\n" + diff --git a/proto/bitcoin/bitcoind/v1alpha/bitcoin.proto b/proto/bitcoin/bitcoind/v1alpha/bitcoin.proto index d17b3df..b08bb08 100644 --- a/proto/bitcoin/bitcoind/v1alpha/bitcoin.proto +++ b/proto/bitcoin/bitcoind/v1alpha/bitcoin.proto @@ -622,6 +622,8 @@ message ImportDescriptorsRequest { uint32 range_start = 3; uint32 range_end = 4; + // If a ranged descriptor is set to active, this specifies the next index to generate addresses from + uint32 next_index = 8; // Nil passes 'now' to Bitcoin Core, which bypasses scanning. google.protobuf.Timestamp timestamp = 5; diff --git a/server/commands/bitcoind.go b/server/commands/bitcoind.go index e8ba8df..3e44890 100644 --- a/server/commands/bitcoind.go +++ b/server/commands/bitcoind.go @@ -48,3 +48,19 @@ type UtxoUpdatePsbt struct { type JoinPsbts struct { Psbts []string `json:"psbts"` } + +// ImportDescriptorsRequestItem represents a single descriptor import request +type ImportDescriptorsRequestItem struct { + Descriptor *string `json:"desc,omitempty"` + Active *bool `json:"active,omitempty"` + Range interface{} `json:"range,omitempty"` // Can be int or [int,int] + NextIndex *int `json:"next_index,omitempty"` + Timestamp interface{} `json:"timestamp"` // Can be int64 or "now" + Internal *bool `json:"internal,omitempty"` + Label *string `json:"label,omitempty"` +} + +// ImportDescriptorsCmd defines the importdescriptors JSON-RPC command +type ImportDescriptorsCmd struct { + Requests []ImportDescriptorsRequestItem `json:"requests"` +} diff --git a/server/server.go b/server/server.go index 7531000..b543354 100644 --- a/server/server.go +++ b/server/server.go @@ -36,7 +36,7 @@ import ( ) func init() { - btcjson.MustRegisterCmd("importdescriptors", new(btcjson.ImportMultiCmd), btcjson.UFWalletOnly) + btcjson.MustRegisterCmd("importdescriptors", new(commands.ImportDescriptorsCmd), btcjson.UFWalletOnly) btcjson.MustRegisterCmd("bumpfee", new(commands.BumpFee), btcjson.UFWalletOnly) btcjson.MustRegisterCmd("analyzepsbt", new(commands.AnalyzePsbt), btcjson.UFWalletOnly) btcjson.MustRegisterCmd("combinepsbt", new(commands.CombinePsbt), btcjson.UFWalletOnly) @@ -1175,28 +1175,27 @@ func (b *Bitcoind) ImportDescriptors(ctx context.Context, c *connect.Request[pb. return withCancel( ctx, b.conf, func(ctx context.Context) ([]parsedDescriptorResponse, error) { - cmd := btcjson.ImportMultiCmd{ - Requests: lo.Map(c.Msg.Requests, func(req *pb.ImportDescriptorsRequest_Request, idx int) btcjson.ImportMultiRequest { - importReq := btcjson.ImportMultiRequest{ + cmd := commands.ImportDescriptorsCmd{ + Requests: lo.Map(c.Msg.Requests, func(req *pb.ImportDescriptorsRequest_Request, idx int) commands.ImportDescriptorsRequestItem { + importReq := commands.ImportDescriptorsRequestItem{ Descriptor: &req.Descriptor_, - Timestamp: lo.If(req.Timestamp == nil, - btcjson.TimestampOrNow{ - Value: "now", - }). - ElseF(func() btcjson.TimestampOrNow { - return btcjson.TimestampOrNow{ - Value: req.Timestamp.AsTime().Unix(), - } - }), + } + + // Set timestamp + if req.Timestamp == nil { + importReq.Timestamp = "now" + } else { + importReq.Timestamp = req.Timestamp.AsTime().Unix() } // Add range if specified if req.RangeStart != 0 || req.RangeEnd != 0 { - // Use [start, end] format - rangeVal := &btcjson.DescriptorRange{ - Value: []int{int(req.RangeStart), int(req.RangeEnd)}, - } - importReq.Range = rangeVal + importReq.Range = []int{int(req.RangeStart), int(req.RangeEnd)} + } + + if req.NextIndex != 0 { + nextIndex := int(req.NextIndex) + importReq.NextIndex = &nextIndex } // Add internal flag @@ -1211,6 +1210,11 @@ func (b *Bitcoind) ImportDescriptors(ctx context.Context, c *connect.Request[pb. importReq.Label = &label } + if req.Active { + active := req.Active + importReq.Active = &active + } + return importReq }), }