+21
-38
bundle/manager.go
+21
-38
bundle/manager.go
···
1077
}
1078
1079
// GetDIDOperations retrieves all operations for a DID (bundles + mempool combined)
1080
-
func (m *Manager) GetDIDOperations(ctx context.Context, did string, verbose bool) ([]plcclient.PLCOperation, error) {
1081
if err := plcclient.ValidateDIDFormat(did); err != nil {
1082
-
return nil, err
1083
}
1084
1085
// Set verbose mode
···
1087
m.didIndex.SetVerbose(verbose)
1088
}
1089
1090
-
// Get bundled operations from DID index
1091
-
bundledOps, err := m.didIndex.GetDIDOperations(ctx, did, m)
1092
if err != nil {
1093
-
return nil, err
1094
}
1095
1096
// Get mempool operations
1097
mempoolOps, err := m.GetDIDOperationsFromMempool(did)
1098
if err != nil {
1099
-
return nil, err
1100
}
1101
1102
if len(mempoolOps) > 0 && verbose {
1103
m.logger.Printf("DEBUG: Found %d operations in mempool", len(mempoolOps))
1104
}
1105
1106
-
// Combine and sort
1107
allOps := append(bundledOps, mempoolOps...)
1108
1109
sort.Slice(allOps, func(i, j int) bool {
1110
return allOps[i].CreatedAt.Before(allOps[j].CreatedAt)
1111
})
1112
1113
-
return allOps, nil
1114
}
1115
1116
// GetDIDOperationsFromMempool retrieves operations for a DID from mempool only
···
1141
1142
// Delegate to DID index for bundled operations
1143
return m.didIndex.GetLatestDIDOperation(ctx, did, m)
1144
-
}
1145
-
1146
-
// GetDIDOperationsWithLocations returns operations along with their bundle/position info
1147
-
func (m *Manager) GetDIDOperationsWithLocations(ctx context.Context, did string, verbose bool) ([]PLCOperationWithLocation, error) {
1148
-
if err := plcclient.ValidateDIDFormat(did); err != nil {
1149
-
return nil, err
1150
-
}
1151
-
1152
-
// Set verbose mode
1153
-
if m.didIndex != nil {
1154
-
m.didIndex.SetVerbose(verbose)
1155
-
}
1156
-
1157
-
// Delegate to DID index
1158
-
results, err := m.didIndex.GetDIDOperationsWithLocations(ctx, did, m)
1159
-
if err != nil {
1160
-
return nil, err
1161
-
}
1162
-
1163
-
// Convert to bundle's type
1164
-
bundleResults := make([]PLCOperationWithLocation, len(results))
1165
-
for i, r := range results {
1166
-
bundleResults[i] = PLCOperationWithLocation{
1167
-
Operation: r.Operation,
1168
-
Bundle: r.Bundle,
1169
-
Position: r.Position,
1170
-
}
1171
-
}
1172
-
1173
-
return bundleResults, nil
1174
}
1175
1176
// VerifyChain verifies the entire bundle chain
···
1077
}
1078
1079
// GetDIDOperations retrieves all operations for a DID (bundles + mempool combined)
1080
+
// Returns: operations only, operations with locations, error
1081
+
func (m *Manager) GetDIDOperations(ctx context.Context, did string, verbose bool) ([]plcclient.PLCOperation, []PLCOperationWithLocation, error) {
1082
if err := plcclient.ValidateDIDFormat(did); err != nil {
1083
+
return nil, nil, err
1084
}
1085
1086
// Set verbose mode
···
1088
m.didIndex.SetVerbose(verbose)
1089
}
1090
1091
+
// Get bundled operations from DID index (includes nullified)
1092
+
bundledOpsWithLoc, err := m.didIndex.GetDIDOperations(ctx, did, m)
1093
if err != nil {
1094
+
return nil, nil, err
1095
+
}
1096
+
1097
+
// Convert to bundle types
1098
+
opsWithLoc := make([]PLCOperationWithLocation, len(bundledOpsWithLoc))
1099
+
bundledOps := make([]plcclient.PLCOperation, len(bundledOpsWithLoc))
1100
+
for i, r := range bundledOpsWithLoc {
1101
+
opsWithLoc[i] = PLCOperationWithLocation{
1102
+
Operation: r.Operation,
1103
+
Bundle: r.Bundle,
1104
+
Position: r.Position,
1105
+
}
1106
+
bundledOps[i] = r.Operation
1107
}
1108
1109
// Get mempool operations
1110
mempoolOps, err := m.GetDIDOperationsFromMempool(did)
1111
if err != nil {
1112
+
return nil, nil, err
1113
}
1114
1115
if len(mempoolOps) > 0 && verbose {
1116
m.logger.Printf("DEBUG: Found %d operations in mempool", len(mempoolOps))
1117
}
1118
1119
+
// Combine operations (for the slice return)
1120
allOps := append(bundledOps, mempoolOps...)
1121
1122
sort.Slice(allOps, func(i, j int) bool {
1123
return allOps[i].CreatedAt.Before(allOps[j].CreatedAt)
1124
})
1125
1126
+
return allOps, opsWithLoc, nil
1127
}
1128
1129
// GetDIDOperationsFromMempool retrieves operations for a DID from mempool only
···
1154
1155
// Delegate to DID index for bundled operations
1156
return m.didIndex.GetLatestDIDOperation(ctx, did, m)
1157
}
1158
1159
// VerifyChain verifies the entire bundle chain
+1
-2
cmd/plcbundle/commands/common.go
+1
-2
cmd/plcbundle/commands/common.go
···
36
GetDIDIndexStats() map[string]interface{}
37
GetDIDIndex() *didindex.Manager
38
BuildDIDIndex(ctx context.Context, progress func(int, int)) error
39
-
GetDIDOperations(ctx context.Context, did string, verbose bool) ([]plcclient.PLCOperation, error)
40
-
GetDIDOperationsWithLocations(ctx context.Context, did string, verbose bool) ([]bundle.PLCOperationWithLocation, error)
41
GetDIDOperationsFromMempool(did string) ([]plcclient.PLCOperation, error)
42
GetLatestDIDOperation(ctx context.Context, did string) (*plcclient.PLCOperation, error)
43
LoadOperation(ctx context.Context, bundleNum, position int) (*plcclient.PLCOperation, error)
···
36
GetDIDIndexStats() map[string]interface{}
37
GetDIDIndex() *didindex.Manager
38
BuildDIDIndex(ctx context.Context, progress func(int, int)) error
39
+
GetDIDOperations(ctx context.Context, did string, verbose bool) ([]plcclient.PLCOperation, []PLCOperationWithLocation, error)
40
GetDIDOperationsFromMempool(did string) ([]plcclient.PLCOperation, error)
41
GetLatestDIDOperation(ctx context.Context, did string) (*plcclient.PLCOperation, error)
42
LoadOperation(ctx context.Context, bundleNum, position int) (*plcclient.PLCOperation, error)
+7
-8
cmd/plcbundle/commands/did.go
+7
-8
cmd/plcbundle/commands/did.go
···
73
• DID: did:plc:524tuhdhh3m7li5gycdn6boe
74
• Handle: tree.fail (resolves via configured resolver)
75
76
-
Requires DID index to be built. If not available, will fall back to
77
-
full scan (slow).`,
78
79
Example: ` # Lookup by DID
80
plcbundle did lookup did:plc:524tuhdhh3m7li5gycdn6boe
81
82
-
# Lookup by handle (requires --resolver-url)
83
plcbundle did lookup tree.fail
84
plcbundle did lookup ngerakines.me
85
···
114
115
// Lookup operations
116
lookupStart := time.Now()
117
-
opsWithLoc, err := mgr.GetDIDOperationsWithLocations(ctx, did, verbose)
118
if err != nil {
119
return err
120
}
···
298
ctx := context.Background()
299
300
// Get all operations with locations
301
-
opsWithLoc, err := mgr.GetDIDOperationsWithLocations(ctx, did, verbose)
302
if err != nil {
303
return err
304
}
···
629
ctx := context.Background()
630
631
// Get operations
632
-
opsWithLoc, err := mgr.GetDIDOperationsWithLocations(ctx, did, false)
633
if err != nil {
634
return err
635
}
···
822
errorCount := 0
823
824
for i, did := range dids {
825
-
opsWithLoc, err := mgr.GetDIDOperationsWithLocations(ctx, did, false)
826
if err != nil {
827
errorCount++
828
fmt.Fprintf(output, "%s,error,0,0,0,0\n", did)
···
1110
defer writer.Flush()
1111
1112
for i, did := range dids {
1113
-
opsWithLoc, err := mgr.GetDIDOperationsWithLocations(ctx, did, false)
1114
if err != nil {
1115
errorCount++
1116
if i < 10 { // Only log first few errors
···
73
• DID: did:plc:524tuhdhh3m7li5gycdn6boe
74
• Handle: tree.fail (resolves via configured resolver)
75
76
+
Requires DID index to be built.`,
77
78
Example: ` # Lookup by DID
79
plcbundle did lookup did:plc:524tuhdhh3m7li5gycdn6boe
80
81
+
# Lookup by handle
82
plcbundle did lookup tree.fail
83
plcbundle did lookup ngerakines.me
84
···
113
114
// Lookup operations
115
lookupStart := time.Now()
116
+
_, opsWithLoc, err := mgr.GetDIDOperations(ctx, did, verbose)
117
if err != nil {
118
return err
119
}
···
297
ctx := context.Background()
298
299
// Get all operations with locations
300
+
_, opsWithLoc, err := mgr.GetDIDOperations(ctx, did, verbose)
301
if err != nil {
302
return err
303
}
···
628
ctx := context.Background()
629
630
// Get operations
631
+
_, opsWithLoc, err := mgr.GetDIDOperations(ctx, did, false)
632
if err != nil {
633
return err
634
}
···
821
errorCount := 0
822
823
for i, did := range dids {
824
+
_, opsWithLoc, err := mgr.GetDIDOperations(ctx, did, false)
825
if err != nil {
826
errorCount++
827
fmt.Fprintf(output, "%s,error,0,0,0,0\n", did)
···
1109
defer writer.Flush()
1110
1111
for i, did := range dids {
1112
+
_, opsWithLoc, err := mgr.GetDIDOperations(ctx, did, false)
1113
if err != nil {
1114
errorCount++
1115
if i < 10 { // Only log first few errors
+3
-113
internal/didindex/lookup.go
+3
-113
internal/didindex/lookup.go
···
9
"tangled.org/atscan.net/plcbundle/internal/plcclient"
10
)
11
12
-
// GetDIDOperations retrieves all operations for a DID from bundles
13
-
func (dim *Manager) GetDIDOperations(ctx context.Context, did string, provider BundleProvider) ([]plcclient.PLCOperation, error) {
14
-
if err := plcclient.ValidateDIDFormat(did); err != nil {
15
-
return nil, err
16
-
}
17
-
18
-
if !dim.Exists() {
19
-
return nil, fmt.Errorf("DID index not available - run 'plcbundle index build' to enable DID lookups")
20
-
}
21
-
22
-
if dim.verbose {
23
-
dim.logger.Printf("DEBUG: Using DID index for lookup")
24
-
}
25
-
26
-
locations, err := dim.GetDIDLocations(did)
27
-
if err != nil {
28
-
return nil, err
29
-
}
30
-
31
-
if len(locations) == 0 {
32
-
return []plcclient.PLCOperation{}, nil
33
-
}
34
-
35
-
// Filter nullified
36
-
var validLocations []OpLocation
37
-
for _, loc := range locations {
38
-
if !loc.Nullified() {
39
-
validLocations = append(validLocations, loc)
40
-
}
41
-
}
42
-
43
-
if dim.verbose {
44
-
dim.logger.Printf("DEBUG: Filtered %d valid locations (from %d total)",
45
-
len(validLocations), len(locations))
46
-
}
47
-
48
-
if len(validLocations) == 1 {
49
-
loc := validLocations[0]
50
-
op, err := provider.LoadOperation(ctx, loc.BundleInt(), loc.PositionInt())
51
-
if err != nil {
52
-
return nil, err
53
-
}
54
-
return []plcclient.PLCOperation{*op}, nil
55
-
}
56
-
57
-
// For multiple operations: group by bundle to minimize bundle loads
58
-
bundleMap := make(map[uint16][]uint16)
59
-
for _, loc := range validLocations {
60
-
bundleMap[loc.Bundle()] = append(bundleMap[loc.Bundle()], loc.Position())
61
-
}
62
-
63
-
if dim.verbose {
64
-
dim.logger.Printf("DEBUG: Loading from %d bundle(s)", len(bundleMap))
65
-
}
66
-
67
-
// Load operations
68
-
var allOps []plcclient.PLCOperation
69
-
for bundleNum, positions := range bundleMap {
70
-
// Optimization: If single position from bundle, use LoadOperation
71
-
if len(positions) == 1 {
72
-
op, err := provider.LoadOperation(ctx, int(bundleNum), int(positions[0]))
73
-
if err != nil {
74
-
dim.logger.Printf("Warning: failed to load operation at bundle %d position %d: %v",
75
-
bundleNum, positions[0], err)
76
-
continue
77
-
}
78
-
allOps = append(allOps, *op)
79
-
} else {
80
-
// Multiple positions: load full bundle
81
-
bundle, err := provider.LoadBundleForDIDIndex(ctx, int(bundleNum))
82
-
if err != nil {
83
-
dim.logger.Printf("Warning: failed to load bundle %d: %v", bundleNum, err)
84
-
continue
85
-
}
86
-
87
-
for _, pos := range positions {
88
-
if int(pos) < len(bundle.Operations) {
89
-
allOps = append(allOps, bundle.Operations[pos])
90
-
}
91
-
}
92
-
}
93
-
}
94
-
95
-
if dim.verbose {
96
-
dim.logger.Printf("DEBUG: Loaded %d total operations", len(allOps))
97
-
}
98
-
99
-
// Sort by time
100
-
sort.Slice(allOps, func(i, j int) bool {
101
-
return allOps[i].CreatedAt.Before(allOps[j].CreatedAt)
102
-
})
103
-
104
-
return allOps, nil
105
-
}
106
-
107
-
// GetDIDOperationsWithLocations returns operations with their bundle/position metadata
108
-
func (dim *Manager) GetDIDOperationsWithLocations(ctx context.Context, did string, provider BundleProvider) ([]OpLocationWithOperation, error) {
109
if err := plcclient.ValidateDIDFormat(did); err != nil {
110
return nil, err
111
}
···
114
return nil, fmt.Errorf("DID index not available - run 'plcbundle index build' to enable DID lookups")
115
}
116
117
-
if dim.verbose {
118
-
dim.logger.Printf("DEBUG: Using DID index for lookup with locations")
119
-
}
120
-
121
locations, err := dim.GetDIDLocations(did)
122
if err != nil {
123
return nil, err
···
127
return []OpLocationWithOperation{}, nil
128
}
129
130
-
if dim.verbose {
131
-
dim.logger.Printf("DEBUG: Found %d locations in index", len(locations))
132
-
}
133
-
134
// Group by bundle
135
bundleMap := make(map[uint16][]OpLocation)
136
for _, loc := range locations {
137
bundleMap[loc.Bundle()] = append(bundleMap[loc.Bundle()], loc)
138
-
}
139
-
140
-
if dim.verbose {
141
-
dim.logger.Printf("DEBUG: Loading from %d bundle(s)", len(bundleMap))
142
}
143
144
var results []OpLocationWithOperation
···
167
sort.Slice(results, func(i, j int) bool {
168
return results[i].Operation.CreatedAt.Before(results[j].Operation.CreatedAt)
169
})
170
-
171
-
if dim.verbose {
172
-
dim.logger.Printf("DEBUG: Loaded %d total operations", len(results))
173
-
}
174
175
return results, nil
176
}
···
9
"tangled.org/atscan.net/plcbundle/internal/plcclient"
10
)
11
12
+
// GetDIDOperations retrieves all operations for a DID WITH location metadata
13
+
// Returns operations with bundle/position info (includes nullified operations)
14
+
func (dim *Manager) GetDIDOperations(ctx context.Context, did string, provider BundleProvider) ([]OpLocationWithOperation, error) {
15
if err := plcclient.ValidateDIDFormat(did); err != nil {
16
return nil, err
17
}
···
20
return nil, fmt.Errorf("DID index not available - run 'plcbundle index build' to enable DID lookups")
21
}
22
23
locations, err := dim.GetDIDLocations(did)
24
if err != nil {
25
return nil, err
···
29
return []OpLocationWithOperation{}, nil
30
}
31
32
// Group by bundle
33
bundleMap := make(map[uint16][]OpLocation)
34
for _, loc := range locations {
35
bundleMap[loc.Bundle()] = append(bundleMap[loc.Bundle()], loc)
36
}
37
38
var results []OpLocationWithOperation
···
61
sort.Slice(results, func(i, j int) bool {
62
return results[i].Operation.CreatedAt.Before(results[j].Operation.CreatedAt)
63
})
64
65
return results, nil
66
}
+2
-3
server/handlers.go
+2
-3
server/handlers.go
···
815
return
816
}
817
818
-
operations, err := s.manager.GetDIDOperations(context.Background(), did, false)
819
if err != nil {
820
sendJSON(w, 500, map[string]string{"error": err.Error()})
821
return
···
842
843
func (s *Server) handleDIDAuditLog(input string) http.HandlerFunc {
844
return func(w http.ResponseWriter, r *http.Request) {
845
-
// Resolve handle to DID
846
did, _, err := s.manager.ResolveHandleOrDID(r.Context(), input)
847
if err != nil {
848
sendJSON(w, 400, map[string]string{"error": err.Error()})
849
return
850
}
851
852
-
operations, err := s.manager.GetDIDOperations(context.Background(), did, false)
853
if err != nil {
854
sendJSON(w, 500, map[string]string{"error": err.Error()})
855
return
···
815
return
816
}
817
818
+
operations, _, err := s.manager.GetDIDOperations(context.Background(), did, false)
819
if err != nil {
820
sendJSON(w, 500, map[string]string{"error": err.Error()})
821
return
···
842
843
func (s *Server) handleDIDAuditLog(input string) http.HandlerFunc {
844
return func(w http.ResponseWriter, r *http.Request) {
845
did, _, err := s.manager.ResolveHandleOrDID(r.Context(), input)
846
if err != nil {
847
sendJSON(w, 400, map[string]string{"error": err.Error()})
848
return
849
}
850
851
+
operations, _, err := s.manager.GetDIDOperations(context.Background(), did, false)
852
if err != nil {
853
sendJSON(w, 500, map[string]string{"error": err.Error()})
854
return