Skip to content

Commit 31e1dab

Browse files
committed
Make GetVtxos accept multiple addresses
1 parent e37e8f8 commit 31e1dab

File tree

9 files changed

+426
-315
lines changed

9 files changed

+426
-315
lines changed

api-spec/openapi/swagger/ark/v1/indexer.swagger.json

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@
310310
]
311311
}
312312
},
313-
"/v1/getVtxos/{address}": {
313+
"/v1/getVtxos/{addresses}": {
314314
"get": {
315315
"operationId": "IndexerService_GetVtxos",
316316
"responses": {
@@ -329,10 +329,15 @@
329329
},
330330
"parameters": [
331331
{
332-
"name": "address",
332+
"name": "addresses",
333333
"in": "path",
334334
"required": true,
335-
"type": "string"
335+
"type": "array",
336+
"items": {
337+
"type": "string"
338+
},
339+
"collectionFormat": "csv",
340+
"minItems": 1
336341
},
337342
{
338343
"name": "spendableOnly",

api-spec/protobuf/ark/v1/indexer.proto

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ service IndexerService {
3737
};
3838
rpc GetVtxos(GetVtxosRequest) returns (GetVtxosResponse) {
3939
option (google.api.http) = {
40-
get: "/v1/getVtxos/{address}"
40+
get: "/v1/getVtxos/{addresses}"
4141
};
4242
};
4343
rpc GetTransactionHistory(GetTransactionHistoryRequest) returns (GetTransactionHistoryResponse) {
@@ -121,7 +121,7 @@ message GetVtxoTreeLeavesResponse {
121121
}
122122

123123
message GetVtxosRequest {
124-
string address = 1;
124+
repeated string addresses = 1;
125125
bool spendable_only = 2;
126126
bool spent_only = 3;
127127
IndexerPageRequest page = 4;

api-spec/protobuf/gen/ark/v1/indexer.pb.go

Lines changed: 283 additions & 283 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api-spec/protobuf/gen/ark/v1/indexer.pb.gw.go

Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/internal/core/application/indexer.go

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ type IndexerService interface {
3232
GetVtxoTreeLeaves(ctx context.Context, batchOutpoint Outpoint, page *Page) (*VtxoTreeLeavesResp, error)
3333
GetForfeitTxs(ctx context.Context, txid string, page *Page) (*ForfeitTxsResp, error)
3434
GetConnectors(ctx context.Context, txid string, page *Page) (*ConnectorResp, error)
35-
GetVtxos(ctx context.Context, pubkey string, spendableOnly, spendOnly bool, page *Page) (*SpendableVtxosResp, error)
35+
GetVtxos(ctx context.Context, pubkeys []string, spendableOnly, spendOnly bool, page *Page) (*SpendableVtxosResp, error)
3636
GetTransactionHistory(ctx context.Context, pubkey string, start, end int64, page *Page) (*TxHistoryResp, error)
3737
GetVtxoChain(ctx context.Context, vtxoKey Outpoint, page *Page) (*VtxoChainResp, error)
3838
GetVirtualTxs(ctx context.Context, txids []string, page *Page) (*VirtualTxsResp, error)
@@ -160,28 +160,23 @@ func (i *indexerService) GetConnectors(ctx context.Context, txid string, page *P
160160
}
161161

162162
func (i *indexerService) GetVtxos(
163-
ctx context.Context, pubkey string, spendableOnly, spentOnly bool, page *Page,
163+
ctx context.Context, pubkeys []string, spendableOnly, spentOnly bool, page *Page,
164164
) (*SpendableVtxosResp, error) {
165165
if spendableOnly && spentOnly {
166166
return nil, fmt.Errorf("spendable and spent only can't be true at the same time")
167167
}
168-
spendable, spent, err := i.repoManager.Vtxos().GetAllVtxosWithPubKey(ctx, pubkey)
168+
169+
vtxos, err := i.repoManager.Vtxos().GetAllVtxosWithPubKeys(
170+
ctx, pubkeys, spendableOnly, spentOnly,
171+
)
169172
if err != nil {
170173
return nil, err
171174
}
172175

173-
vtxos := append(spendable, spent...)
174-
if spendableOnly {
175-
vtxos = spendable
176-
}
177-
if spentOnly {
178-
vtxos = spent
179-
}
180-
181-
spendableVtxosPaged, pageResp := paginate(vtxos, page, maxPageSizeSpendableVtxos)
176+
pagedVtxos, pageResp := paginate(vtxos, page, maxPageSizeSpendableVtxos)
182177

183178
return &SpendableVtxosResp{
184-
Vtxos: spendableVtxosPaged,
179+
Vtxos: pagedVtxos,
185180
Page: pageResp,
186181
}, nil
187182
}

server/internal/core/domain/round_repo.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ type VtxoRepository interface {
4141
GetSpendableVtxosWithPubKey(ctx context.Context, pubkey string) ([]Vtxo, error)
4242
GetAll(ctx context.Context) ([]Vtxo, error)
4343
GetAllVtxosWithPubKey(ctx context.Context, pubkey string) ([]Vtxo, []Vtxo, error)
44+
GetAllVtxosWithPubKeys(ctx context.Context, pubkeys []string, spendableOnly, spentOnly bool) ([]Vtxo, error)
4445
UpdateExpireAt(ctx context.Context, vtxos []VtxoKey, expireAt int64) error
4546
GetLeafVtxosForRound(ctx context.Context, txid string) ([]Vtxo, error)
4647
Close()

server/internal/infrastructure/db/badger/vtxo_repo.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"errors"
66
"fmt"
77
"path/filepath"
8+
"sort"
89
"strings"
910
"time"
1011

@@ -209,6 +210,47 @@ func (r *vtxoRepository) GetAllVtxosWithPubKey(
209210
return unspentVtxos, spentVtxos, nil
210211
}
211212

213+
func (r *vtxoRepository) GetAllVtxosWithPubKeys(
214+
ctx context.Context, pubkeys []string, spendableOnly, spentOnly bool,
215+
) ([]domain.Vtxo, error) {
216+
if spendableOnly && spendableOnly == spentOnly {
217+
return nil, fmt.Errorf("spendable and spent only can't be true at the same time")
218+
}
219+
220+
allVtxos := make([]domain.Vtxo, 0)
221+
for _, pubkey := range pubkeys {
222+
query := badgerhold.Where("PubKey").Eq(pubkey)
223+
vtxos, err := r.findVtxos(ctx, query)
224+
if err != nil {
225+
return nil, err
226+
}
227+
sort.SliceStable(vtxos, func(i, j int) bool {
228+
return vtxos[i].CreatedAt > vtxos[j].CreatedAt
229+
})
230+
231+
if spendableOnly {
232+
spendableVtxos := make([]domain.Vtxo, 0, len(vtxos))
233+
for _, vtxo := range vtxos {
234+
if !vtxo.Spent && !vtxo.Swept && !vtxo.Redeemed {
235+
spendableVtxos = append(spendableVtxos, vtxo)
236+
}
237+
}
238+
vtxos = spendableVtxos
239+
}
240+
if spentOnly {
241+
spentVtxos := make([]domain.Vtxo, 0, len(vtxos))
242+
for _, vtxo := range vtxos {
243+
if vtxo.Spent || vtxo.Swept || vtxo.Redeemed {
244+
spentVtxos = append(spentVtxos, vtxo)
245+
}
246+
}
247+
vtxos = spentVtxos
248+
}
249+
allVtxos = append(allVtxos, vtxos...)
250+
}
251+
return allVtxos, nil
252+
}
253+
212254
func (r *vtxoRepository) Close() {
213255
r.store.Close()
214256
}

server/internal/infrastructure/db/sqlite/vtxo_repo.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"database/sql"
66
"fmt"
7+
"sort"
78

89
"github.com/ark-network/ark/server/internal/core/domain"
910
"github.com/ark-network/ark/server/internal/infrastructure/db/sqlite/sqlc/queries"
@@ -330,6 +331,58 @@ func (v *vtxoRepository) GetAllVtxosWithPubKey(
330331
return unspentVtxos, spentVtxos, nil
331332
}
332333

334+
func (v *vtxoRepository) GetAllVtxosWithPubKeys(
335+
ctx context.Context, pubkeys []string, spendableOnly, spentOnly bool,
336+
) ([]domain.Vtxo, error) {
337+
if spendableOnly && spendableOnly == spentOnly {
338+
return nil, fmt.Errorf("spendable and spent only can't be true at the same time")
339+
}
340+
341+
allVtxos := make([]domain.Vtxo, 0)
342+
// TODO: make this a proper sql query
343+
for _, pubkey := range pubkeys {
344+
res, err := v.querier.SelectVtxosWithPubkey(ctx, pubkey)
345+
if err != nil {
346+
return nil, err
347+
}
348+
rows := make([]queries.Vtxo, 0, len(res))
349+
for _, row := range res {
350+
rows = append(rows, row.Vtxo)
351+
}
352+
353+
vtxos, err := readRows(rows)
354+
if err != nil {
355+
return nil, err
356+
}
357+
sort.SliceStable(vtxos, func(i, j int) bool {
358+
return vtxos[i].CreatedAt > vtxos[j].CreatedAt
359+
})
360+
361+
if spendableOnly {
362+
spendableVtxos := make([]domain.Vtxo, 0, len(vtxos))
363+
for _, vtxo := range vtxos {
364+
if !vtxo.Spent && !vtxo.Swept && !vtxo.Redeemed {
365+
spendableVtxos = append(spendableVtxos, vtxo)
366+
}
367+
}
368+
vtxos = spendableVtxos
369+
}
370+
if spentOnly {
371+
spentVtxos := make([]domain.Vtxo, 0, len(vtxos))
372+
for _, vtxo := range vtxos {
373+
if vtxo.Spent || vtxo.Swept || vtxo.Redeemed {
374+
spentVtxos = append(spentVtxos, vtxo)
375+
}
376+
}
377+
vtxos = spentVtxos
378+
}
379+
380+
allVtxos = append(allVtxos, vtxos...)
381+
}
382+
383+
return allVtxos, nil
384+
}
385+
333386
func rowToVtxo(row queries.Vtxo) domain.Vtxo {
334387
return domain.Vtxo{
335388
VtxoKey: domain.VtxoKey{

server/internal/interface/grpc/handlers/indexer.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ func (e indexerService) GetConnectors(ctx context.Context, request *arkv1.GetCon
202202
}
203203

204204
func (e indexerService) GetVtxos(ctx context.Context, request *arkv1.GetVtxosRequest) (*arkv1.GetVtxosResponse, error) {
205-
pubkey, err := parseArkAddress(request.GetAddress())
205+
pubkeys, err := parseArkAddresses(request.GetAddresses())
206206
if err != nil {
207207
return nil, status.Error(codes.InvalidArgument, err.Error())
208208
}
@@ -215,7 +215,7 @@ func (e indexerService) GetVtxos(ctx context.Context, request *arkv1.GetVtxosReq
215215
}
216216

217217
resp, err := e.indexerSvc.GetVtxos(
218-
ctx, pubkey, request.GetSpendableOnly(), request.GetSpentOnly(), page,
218+
ctx, pubkeys, request.GetSpendableOnly(), request.GetSpentOnly(), page,
219219
)
220220
if err != nil {
221221
return nil, status.Errorf(codes.Internal, "failed to get vtxos: %v", err)
@@ -458,3 +458,18 @@ func protoPage(page application.PageResp) *arkv1.IndexerPageResponse {
458458
Total: page.Total,
459459
}
460460
}
461+
462+
func parseArkAddresses(addresses []string) ([]string, error) {
463+
if len(addresses) == 0 {
464+
return nil, fmt.Errorf("missing addresses")
465+
}
466+
pubkeys := make([]string, 0, len(addresses))
467+
for _, address := range addresses {
468+
pubkey, err := parseArkAddress(address)
469+
if err != nil {
470+
return nil, err
471+
}
472+
pubkeys = append(pubkeys, pubkey)
473+
}
474+
return pubkeys, nil
475+
}

0 commit comments

Comments
 (0)