Skip to content

Commit b2cc958

Browse files
committed
Update app and interface layers
1 parent 2387c06 commit b2cc958

File tree

4 files changed

+324
-349
lines changed

4 files changed

+324
-349
lines changed

server/internal/core/application/indexer.go

Lines changed: 158 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@ const (
2727

2828
type IndexerService interface {
2929
GetCommitmentTxInfo(ctx context.Context, txid string) (*CommitmentTxResp, error)
30+
GetCommitmentTxLeaves(ctx context.Context, txid string, page *Page) (*CommitmentTxLeavesResp, error)
3031
GetVtxoTree(ctx context.Context, batchOutpoint Outpoint, page *Page) (*VtxoTreeResp, error)
32+
GetVtxoTreeLeaves(ctx context.Context, batchOutpoint Outpoint, page *Page) (*VtxoTreeLeavesResp, error)
3133
GetForfeitTxs(ctx context.Context, batchOutpoint Outpoint, page *Page) (*ForfeitTxsResp, error)
3234
GetConnectors(ctx context.Context, batchOutpoint Outpoint, page *Page) (*ConnectorResp, error)
33-
GetSpendableVtxos(ctx context.Context, pubkey string, page *Page) (*SpendableVtxosResp, error)
35+
GetVtxos(ctx context.Context, pubkey string, spendableOnly, spendOnly bool, page *Page) (*SpendableVtxosResp, error)
3436
GetTransactionHistory(ctx context.Context, pubkey string, start, end int64, page *Page) (*TxHistoryResp, error)
3537
GetVtxoChain(ctx context.Context, vtxoKey Outpoint, page *Page) (*VtxoChainResp, error)
3638
GetVirtualTxs(ctx context.Context, txids []string, page *Page) (*VirtualTxsResp, error)
@@ -64,18 +66,34 @@ func (i *indexerService) GetCommitmentTxInfo(
6466
batches := make(map[VOut]Batch)
6567
// TODO: currently commitment tx has only one batch, in future multiple batches will be supported
6668
batches[0] = Batch{
67-
TotalBatchAmount: roundStats.TotalBatchAmount,
68-
TotalForfeitAmount: roundStats.TotalForfeitAmount,
69-
TotalInputVtxos: roundStats.TotalInputVtxos,
70-
TotalOutputVtxos: roundStats.TotalOutputVtxos,
71-
ExpiresAt: roundStats.ExpiresAt,
72-
Swept: roundStats.Swept,
69+
TotalBatchAmount: roundStats.TotalBatchAmount,
70+
TotalOutputVtxos: roundStats.TotalOutputVtxos,
71+
ExpiresAt: roundStats.ExpiresAt,
72+
Swept: roundStats.Swept,
7373
}
7474

7575
return &CommitmentTxResp{
76-
StartedAt: roundStats.Started,
77-
EndAt: roundStats.Ended,
78-
Batches: batches,
76+
StartedAt: roundStats.Started,
77+
EndAt: roundStats.Ended,
78+
Batches: batches,
79+
TotalInputAmount: roundStats.TotalForfeitAmount,
80+
TotalInputtVtxos: roundStats.TotalInputVtxos,
81+
TotalOutputVtxos: roundStats.TotalOutputVtxos,
82+
TotalOutputAmount: roundStats.TotalBatchAmount,
83+
}, nil
84+
}
85+
86+
func (i *indexerService) GetCommitmentTxLeaves(ctx context.Context, txid string, page *Page) (*CommitmentTxLeavesResp, error) {
87+
leaves, err := i.repoManager.Vtxos().GetLeafVtxosForRound(ctx, txid)
88+
if err != nil {
89+
return nil, err
90+
}
91+
92+
paginatedLeaves, pageResp := paginate(leaves, page, maxPageSizeVtxoTree)
93+
94+
return &CommitmentTxLeavesResp{
95+
Leaves: paginatedLeaves,
96+
Page: pageResp,
7997
}, nil
8098
}
8199

@@ -93,6 +111,20 @@ func (i *indexerService) GetVtxoTree(ctx context.Context, batchOutpoint Outpoint
93111
}, nil
94112
}
95113

114+
func (i *indexerService) GetVtxoTreeLeaves(ctx context.Context, outpoint Outpoint, page *Page) (*VtxoTreeLeavesResp, error) {
115+
leaves, err := i.repoManager.Vtxos().GetLeafVtxosForRound(ctx, outpoint.Txid)
116+
if err != nil {
117+
return nil, err
118+
}
119+
120+
paginatedLeaves, pageResp := paginate(leaves, page, maxPageSizeVtxoTree)
121+
122+
return &VtxoTreeLeavesResp{
123+
Leaves: paginatedLeaves,
124+
Page: pageResp,
125+
}, nil
126+
}
127+
96128
func (i *indexerService) GetForfeitTxs(ctx context.Context, batchOutpoint Outpoint, page *Page) (*ForfeitTxsResp, error) {
97129
forfeitTxs, err := i.repoManager.Rounds().GetRoundForfeitTxs(ctx, batchOutpoint.Txid) //TODO batch thing
98130
if err != nil {
@@ -127,12 +159,25 @@ func (i *indexerService) GetConnectors(ctx context.Context, batchOutpoint Outpoi
127159
}, nil
128160
}
129161

130-
func (i *indexerService) GetSpendableVtxos(ctx context.Context, pubkey string, page *Page) (*SpendableVtxosResp, error) {
131-
vtxos, err := i.repoManager.Vtxos().GetSpendableVtxosWithPubKey(ctx, pubkey)
162+
func (i *indexerService) GetVtxos(
163+
ctx context.Context, pubkey string, spendableOnly, spentOnly bool, page *Page,
164+
) (*SpendableVtxosResp, error) {
165+
if spendableOnly && spentOnly {
166+
return nil, fmt.Errorf("spendable and spent only can't be true at the same time")
167+
}
168+
spendable, spent, err := i.repoManager.Vtxos().GetAllVtxosWithPubKey(ctx, pubkey)
132169
if err != nil {
133170
return nil, err
134171
}
135172

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

138183
return &SpendableVtxosResp{
@@ -149,7 +194,19 @@ func (i *indexerService) GetTransactionHistory(
149194
return nil, err
150195
}
151196

152-
txs, err := vtxosToTxs(spendable, spent)
197+
var roundTxids map[string]any
198+
if len(spent) > 0 {
199+
txids := make([]string, 0, len(spent))
200+
for _, vtxo := range spent {
201+
txids = append(txids, vtxo.SpentBy)
202+
}
203+
roundTxids, err = i.repoManager.Rounds().GetExistingRounds(ctx, txids)
204+
if err != nil {
205+
return nil, err
206+
}
207+
}
208+
209+
txs, err := vtxosToTxs(spendable, spent, roundTxids)
153210
if err != nil {
154211
return nil, err
155212
}
@@ -158,8 +215,8 @@ func (i *indexerService) GetTransactionHistory(
158215
txsPaged, pageResp := paginate(txs, page, maxPageSizeTxHistory)
159216

160217
return &TxHistoryResp{
161-
Records: txsPaged,
162-
Pagination: pageResp,
218+
Records: txsPaged,
219+
Page: pageResp,
163220
}, nil
164221
}
165222

@@ -188,20 +245,24 @@ func filterByDate(txs []TxHistoryRecord, start, end int64) []TxHistoryRecord {
188245
return filteredTxs
189246
}
190247

191-
type vtxoKeyWithCreatedAt struct {
192-
domain.VtxoKey
193-
CreatedAt int64
194-
}
195-
196248
func (i *indexerService) GetVtxoChain(ctx context.Context, vtxoKey Outpoint, page *Page) (*VtxoChainResp, error) {
197249
chainMap := make(map[vtxoKeyWithCreatedAt]ChainWithExpiry)
198250

199251
outpoint := domain.VtxoKey{
200252
Txid: vtxoKey.Txid,
201253
VOut: vtxoKey.Vout,
202254
}
255+
vtxos, err := i.repoManager.Vtxos().GetVtxos(ctx, []domain.VtxoKey{outpoint})
256+
if err != nil {
257+
return nil, err
258+
}
203259

204-
if err := i.buildChain(ctx, outpoint, chainMap, true); err != nil {
260+
if len(vtxos) == 0 {
261+
return nil, fmt.Errorf("vtxo not found for outpoint: %v", outpoint)
262+
}
263+
vtxo := vtxos[0]
264+
265+
if err := i.buildChain(ctx, vtxo, chainMap); err != nil {
205266
return nil, err
206267
}
207268

@@ -211,44 +272,36 @@ func (i *indexerService) GetVtxoChain(ctx context.Context, vtxoKey Outpoint, pag
211272
}
212273

213274
sort.Slice(chainSlice, func(i, j int) bool {
214-
return chainSlice[i].CreatedAt < chainSlice[j].CreatedAt
275+
return chainSlice[i].CreatedAt > chainSlice[j].CreatedAt
215276
})
216277

217278
pagedChainSlice, pageResp := paginate(chainSlice, page, maxPageSizeVtxoChain)
218279

219-
txMap := make(map[string]ChainWithExpiry)
280+
chain := make([]ChainWithExpiry, 0, len(pagedChainSlice))
220281
for _, vtxo := range pagedChainSlice {
221-
txMap[vtxo.Txid] = chainMap[vtxo]
282+
chain = append(chain, chainMap[vtxo])
222283
}
223284

224285
return &VtxoChainResp{
225-
Transactions: txMap,
226-
Page: pageResp,
286+
Chain: chain,
287+
Page: pageResp,
288+
RootCommitmentTxid: vtxo.RoundTxid,
289+
Depth: getMaxDepth(chainMap),
227290
}, nil
228291
}
229292

230293
func (i *indexerService) buildChain(
231294
ctx context.Context,
232-
outpoint domain.VtxoKey,
295+
vtxo domain.Vtxo,
233296
chain map[vtxoKeyWithCreatedAt]ChainWithExpiry,
234-
isFirst bool,
235297
) error {
236-
vtxos, err := i.repoManager.Vtxos().GetVtxos(ctx, []domain.VtxoKey{outpoint})
237-
if err != nil {
238-
return err
239-
}
240-
241-
if isFirst && len(vtxos) == 0 {
242-
return fmt.Errorf("vtxo not found for outpoint: %v", outpoint)
243-
}
244-
245-
vtxo := vtxos[0]
246298
key := vtxoKeyWithCreatedAt{
247-
VtxoKey: outpoint,
299+
VtxoKey: vtxo.VtxoKey,
248300
CreatedAt: vtxo.CreatedAt,
249301
}
250302
if _, ok := chain[key]; !ok {
251303
chain[key] = ChainWithExpiry{
304+
Txid: vtxo.VtxoKey.Txid,
252305
Txs: make([]ChainTx, 0),
253306
ExpiresAt: vtxo.ExpireAt,
254307
}
@@ -264,6 +317,7 @@ func (i *indexerService) buildChain(
264317
Type: "commitment",
265318
})
266319
chain[key] = ChainWithExpiry{
320+
Txid: vtxo.VtxoKey.Txid,
267321
Txs: txs,
268322
ExpiresAt: chain[key].ExpiresAt,
269323
}
@@ -282,15 +336,24 @@ func (i *indexerService) buildChain(
282336
Type: "virtual",
283337
})
284338
chain[key] = ChainWithExpiry{
339+
Txid: chain[key].Txid,
285340
Txs: txs,
286341
ExpiresAt: chain[key].ExpiresAt,
287342
}
288343
parentOutpoint := domain.VtxoKey{
289344
Txid: in.PreviousOutPoint.Hash.String(),
290345
VOut: in.PreviousOutPoint.Index,
291346
}
347+
vtxos, err := i.repoManager.Vtxos().GetVtxos(ctx, []domain.VtxoKey{parentOutpoint})
348+
if err != nil {
349+
return err
350+
}
292351

293-
if err := i.buildChain(ctx, parentOutpoint, chain, false); err != nil {
352+
if len(vtxos) == 0 {
353+
return fmt.Errorf("vtxo not found for outpoint: %v", parentOutpoint)
354+
}
355+
356+
if err := i.buildChain(ctx, vtxos[0], chain); err != nil {
294357
return err
295358
}
296359
}
@@ -319,7 +382,7 @@ func (i *indexerService) GetSweptCommitmentTx(ctx context.Context, txid string)
319382
return &SweptCommitmentTxResp{}, nil
320383
}
321384

322-
func paginate[T any](items []T, params *Page, maxSize int) ([]T, PageResp) {
385+
func paginate[T any](items []T, params *Page, maxSize int32) ([]T, PageResp) {
323386
if params == nil {
324387
return items, PageResp{}
325388
}
@@ -330,8 +393,8 @@ func paginate[T any](items []T, params *Page, maxSize int) ([]T, PageResp) {
330393
params.PageNum = 1
331394
}
332395

333-
totalCount := len(items)
334-
totalPages := int(math.Ceil(float64(totalCount) / float64(params.PageSize)))
396+
totalCount := int32(len(items))
397+
totalPages := int32(math.Ceil(float64(totalCount) / float64(params.PageSize)))
335398
next := min(params.PageNum+1, totalPages)
336399

337400
resp := PageResp{
@@ -374,7 +437,7 @@ func flattenNodes(t [][]tree.Node) []Node {
374437
return result
375438
}
376439

377-
func vtxosToTxs(spendable, spent []domain.Vtxo) ([]TxHistoryRecord, error) {
440+
func vtxosToTxs(spendable, spent []domain.Vtxo, roundTxids map[string]any) ([]TxHistoryRecord, error) {
378441
txs := make([]TxHistoryRecord, 0)
379442

380443
// Receivals
@@ -399,10 +462,14 @@ func vtxosToTxs(spendable, spent []domain.Vtxo) ([]TxHistoryRecord, error) {
399462
commitmentTxid := vtxo.RoundTxid
400463
virtualTxid := ""
401464
settled := !vtxo.IsPending()
465+
settledBy := ""
402466
if vtxo.IsPending() {
403467
virtualTxid = vtxo.Txid
404468
commitmentTxid = ""
405469
settled = vtxo.SpentBy != ""
470+
if _, ok := roundTxids[vtxo.SpentBy]; settled && ok {
471+
settledBy = vtxo.SpentBy
472+
}
406473
}
407474

408475
txs = append(txs, TxHistoryRecord{
@@ -412,6 +479,7 @@ func vtxosToTxs(spendable, spent []domain.Vtxo) ([]TxHistoryRecord, error) {
412479
Type: TxReceived,
413480
CreatedAt: time.Unix(vtxo.CreatedAt, 0),
414481
Settled: settled,
482+
SettledBy: settledBy,
415483
})
416484
}
417485

@@ -524,3 +592,49 @@ func getVtxo(usedVtxos []domain.Vtxo, spentByVtxos []domain.Vtxo) domain.Vtxo {
524592
}
525593
return domain.Vtxo{}
526594
}
595+
596+
type vtxoKeyWithCreatedAt struct {
597+
domain.VtxoKey
598+
CreatedAt int64
599+
}
600+
601+
func getMaxDepth(chainMap map[vtxoKeyWithCreatedAt]ChainWithExpiry) int32 {
602+
memo := make(map[string]int32)
603+
604+
// Create a lookup from txid to ChainWithExpiry
605+
txidToChain := make(map[string]ChainWithExpiry)
606+
for _, chain := range chainMap {
607+
txidToChain[chain.Txid] = chain
608+
}
609+
610+
// DFS function to get depth from a given txid
611+
var dfs func(string) int32
612+
dfs = func(txid string) int32 {
613+
if val, ok := memo[txid]; ok {
614+
return val
615+
}
616+
chain := txidToChain[txid]
617+
if len(chain.Txs) == 1 && chain.Txs[0].Type == "commitment" {
618+
memo[txid] = 1
619+
return 1
620+
}
621+
maxDepth := int32(0)
622+
for _, child := range chain.Txs {
623+
depth := dfs(child.Txid)
624+
maxDepth = max(depth, maxDepth)
625+
}
626+
memo[txid] = maxDepth + 1
627+
return memo[txid]
628+
}
629+
630+
// Compute max depth starting from all root txids in the map
631+
max := int32(0)
632+
for _, chain := range chainMap {
633+
depth := dfs(chain.Txid)
634+
if depth > max {
635+
max = depth
636+
}
637+
}
638+
639+
return max
640+
}

0 commit comments

Comments
 (0)