Skip to content

Commit f8b236c

Browse files
committed
Add object reference support via ref field in protocol
Introduces the object reference and registry pattern for persisting objects across requests. The runner now includes a ref field in requests when the expected result is a reference, allowing handlers to store and reuse objects like contexts, blocks, and chains throughout the tests. Documents the ref field and registry pattern in handler-spec.md.
1 parent b49c1c2 commit f8b236c

File tree

3 files changed

+46
-1
lines changed

3 files changed

+46
-1
lines changed

docs/handler-spec.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,16 @@ Handlers communicate with the test runner via **stdin/stdout**:
1717
{
1818
"id": "unique-request-id",
1919
"method": "method_name",
20-
"params": { /* method-specific parameters */ }
20+
"params": { /* method-specific parameters */ },
21+
"ref": "reference-name"
2122
}
2223
```
2324

2425
**Fields:**
2526
- `id` (string, required): Unique identifier for this request
2627
- `method` (string, required): The operation to perform. Each unique method must be implemented by the handler to exercise the corresponding binding API operation.
2728
- `params` (object, optional): Method-specific parameters
29+
- `ref` (string, optional): Reference name for storing the returned object. Required for methods that return object references (see [Object References and Registry](#object-references-and-registry))
2830

2931
### Response
3032

@@ -59,6 +61,30 @@ Handlers communicate with the test runner via **stdin/stdout**:
5961
4. **Error Handling**: Return error responses for invalid requests or failed operations
6062
5. **Exit Behavior**: Exit cleanly when stdin closes
6163

64+
## Object References and Registry
65+
66+
Many operations return objects (contexts, blocks, chains, etc.) that must persist across requests. The protocol uses named references and a registry pattern:
67+
68+
**Creating Objects**: Methods that return objects require a `ref` field in the request. The handler stores the object in a registry under that name and returns the reference name as the result.
69+
70+
```json
71+
// Request
72+
{"id": "1", "method": "btck_context_create", "params": {...}, "ref": "$ctx1"}
73+
// Response
74+
{"id": "1", "result": "$ctx1", "error": null}
75+
// Handler action: registry["$ctx1"] = created_context_ptr
76+
```
77+
78+
**Using Objects**: When a parameter is marked as `(reference, required)`, the runner passes the reference name and the handler looks it up:
79+
80+
```json
81+
// Request
82+
{"id": "2", "method": "btck_chainstate_manager_create", "params": {"context": "$ctx1"}, "ref": "$csm1"}
83+
// Handler action: Look up registry["$ctx1"], create manager, store as registry["$csm1"]
84+
```
85+
86+
**Implementation**: Handlers must maintain a registry (map of reference names to object pointers) throughout their lifetime. Objects remain alive until explicitly destroyed or handler exit.
87+
6288
## Test Suites and Expected Responses
6389

6490
The conformance tests are organized into suites, each testing a specific aspect of the Bitcoin Kernel bindings. Test files are located in [`../testdata/`](../testdata/).

runner/runner.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,11 @@ func (tr *TestRunner) runTest(ctx context.Context, test TestCase) SingleTestResu
156156
Params: test.Params,
157157
}
158158

159+
// If the expected result is a reference, include it as ref in the request
160+
if ref := extractRefFromExpected(test.Expected); ref != "" {
161+
req.Ref = ref
162+
}
163+
159164
err := tr.SendRequest(req)
160165
if err != nil {
161166
return SingleTestResult{
@@ -241,6 +246,19 @@ func validateResponseForError(test TestCase, resp *Response) error {
241246
return nil
242247
}
243248

249+
// extractRefFromExpected extracts a reference name from the expected result if it's a
250+
// string starting with "$". Returns empty string if not a reference.
251+
func extractRefFromExpected(expected TestExpectation) string {
252+
var resultStr string
253+
if err := json.Unmarshal(expected.Result, &resultStr); err != nil {
254+
return ""
255+
}
256+
if len(resultStr) > 1 && resultStr[0] == '$' {
257+
return resultStr
258+
}
259+
return ""
260+
}
261+
244262
// validateResponseForSuccess validates that a response correctly represents a success case.
245263
// It ensures the response contains no error, and if a result is expected, it matches the
246264
// expected value.

runner/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ type Request struct {
4040
ID string `json:"id"`
4141
Method string `json:"method"`
4242
Params json.RawMessage `json:"params"`
43+
Ref string `json:"ref,omitempty"`
4344
}
4445

4546
// Response represents a response from the handler.

0 commit comments

Comments
 (0)