Skip to content

Commit 9ee85ab

Browse files
committed
Implement conformance handler for v0.0.3 tests
1 parent aa20662 commit 9ee85ab

File tree

16 files changed

+636
-27
lines changed

16 files changed

+636
-27
lines changed

cmd/conformance-handler/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Conformance Handler Makefile
22

33
# Test suite configuration
4-
TEST_VERSION := 0.0.2
4+
TEST_VERSION := 0.0.3-alpha.2
55
TEST_REPO := stringintech/kernel-bindings-tests
66
TEST_DIR := .conformance-tests
77

@@ -71,7 +71,7 @@ test: build
7171
$(MAKE) download-tests; \
7272
fi
7373
@echo "Running conformance tests..."
74-
$(TEST_RUNNER) --handler ./$(HANDLER_BIN)
74+
$(TEST_RUNNER) --handler ./$(HANDLER_BIN) -vv
7575

7676
clean:
7777
@echo "Cleaning up..."

cmd/conformance-handler/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,5 @@ The test suite is automatically downloaded for your platform (darwin_arm64, darw
2929
## Pinned Test Version
3030

3131
This handler is compatible with:
32-
- Test Suite Version: `0.0.2`
32+
- Test Suite Version: `0.0.3-alpha.2`
3333
- Test Repository: [stringintech/kernel-bindings-tests](https://github.com/stringintech/kernel-bindings-tests)

cmd/conformance-handler/block.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package main
2+
3+
import (
4+
"encoding/hex"
5+
"encoding/json"
6+
7+
"github.com/stringintech/go-bitcoinkernel/kernel"
8+
)
9+
10+
// handleBlockCreate creates a block from raw hex data
11+
func handleBlockCreate(registry *Registry, req Request) Response {
12+
var params struct {
13+
RawBlock string `json:"raw_block"`
14+
}
15+
16+
if err := json.Unmarshal(req.Params, &params); err != nil {
17+
return NewInvalidParamsResponse(req.ID, "failed to parse params")
18+
}
19+
20+
if req.Ref == "" {
21+
return NewInvalidParamsResponse(req.ID, "ref field is required")
22+
}
23+
24+
// Decode hex to bytes
25+
blockBytes, err := hex.DecodeString(params.RawBlock)
26+
if err != nil {
27+
return NewInvalidParamsResponse(req.ID, "raw_block must be valid hex")
28+
}
29+
30+
// Create block
31+
block, err := kernel.NewBlock(blockBytes)
32+
if err != nil {
33+
return NewEmptyErrorResponse(req.ID)
34+
}
35+
36+
registry.Store(req.Ref, block)
37+
38+
return NewSuccessResponseWithRef(req.ID, req.Ref)
39+
}
40+
41+
// handleBlockTreeEntryGetBlockHash gets the block hash from a block tree entry
42+
func handleBlockTreeEntryGetBlockHash(registry *Registry, req Request) Response {
43+
var params struct {
44+
BlockTreeEntry string `json:"block_tree_entry"`
45+
}
46+
47+
if err := json.Unmarshal(req.Params, &params); err != nil {
48+
return NewInvalidParamsResponse(req.ID, "failed to parse params")
49+
}
50+
51+
// Get block tree entry from registry
52+
entry, err := registry.GetBlockTreeEntry(params.BlockTreeEntry)
53+
if err != nil {
54+
return NewInvalidParamsResponse(req.ID, err.Error())
55+
}
56+
57+
// Get block hash and convert to string (handles display order conversion)
58+
hashView := entry.Hash()
59+
hashString := hashView.String()
60+
61+
// Return hash as string
62+
return NewSuccessResponse(req.ID, hashString)
63+
}

cmd/conformance-handler/chain.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
)
6+
7+
// handleChainGetHeight gets the current height of the chain
8+
func handleChainGetHeight(registry *Registry, req Request) Response {
9+
var params struct {
10+
Chain string `json:"chain"`
11+
}
12+
13+
if err := json.Unmarshal(req.Params, &params); err != nil {
14+
return NewInvalidParamsResponse(req.ID, "failed to parse params")
15+
}
16+
17+
// Get chain from registry
18+
chain, err := registry.GetChain(params.Chain)
19+
if err != nil {
20+
return NewInvalidParamsResponse(req.ID, err.Error())
21+
}
22+
23+
// Get height
24+
height := chain.GetHeight()
25+
26+
// Return height as integer
27+
return NewSuccessResponse(req.ID, height)
28+
}
29+
30+
// handleChainGetByHeight gets a block tree entry at the specified height
31+
func handleChainGetByHeight(registry *Registry, req Request) Response {
32+
var params struct {
33+
Chain string `json:"chain"`
34+
BlockHeight int32 `json:"block_height"`
35+
}
36+
37+
if err := json.Unmarshal(req.Params, &params); err != nil {
38+
return NewInvalidParamsResponse(req.ID, "failed to parse params")
39+
}
40+
41+
if req.Ref == "" {
42+
return NewInvalidParamsResponse(req.ID, "ref field is required")
43+
}
44+
45+
// Get chain from registry
46+
chain, err := registry.GetChain(params.Chain)
47+
if err != nil {
48+
return NewInvalidParamsResponse(req.ID, err.Error())
49+
}
50+
51+
// Get block tree entry at height
52+
entry := chain.GetByHeight(params.BlockHeight)
53+
if entry == nil {
54+
return NewEmptyErrorResponse(req.ID)
55+
}
56+
57+
registry.Store(req.Ref, entry)
58+
59+
return NewSuccessResponseWithRef(req.ID, req.Ref)
60+
}
61+
62+
// handleChainContains checks if a block tree entry is in the active chain
63+
func handleChainContains(registry *Registry, req Request) Response {
64+
var params struct {
65+
Chain string `json:"chain"`
66+
BlockTreeEntry string `json:"block_tree_entry"`
67+
}
68+
69+
if err := json.Unmarshal(req.Params, &params); err != nil {
70+
return NewInvalidParamsResponse(req.ID, "failed to parse params")
71+
}
72+
73+
// Get chain from registry
74+
chain, err := registry.GetChain(params.Chain)
75+
if err != nil {
76+
return NewInvalidParamsResponse(req.ID, err.Error())
77+
}
78+
79+
// Get block tree entry from registry
80+
entry, err := registry.GetBlockTreeEntry(params.BlockTreeEntry)
81+
if err != nil {
82+
return NewInvalidParamsResponse(req.ID, err.Error())
83+
}
84+
85+
// Check if chain contains the entry
86+
contains := chain.Contains(entry)
87+
88+
// Return boolean result
89+
return NewSuccessResponse(req.ID, contains)
90+
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"os"
6+
"path/filepath"
7+
8+
"github.com/stringintech/go-bitcoinkernel/kernel"
9+
)
10+
11+
// handleChainstateManagerCreate creates a chainstate manager from a context
12+
func handleChainstateManagerCreate(registry *Registry, req Request) Response {
13+
var params struct {
14+
Context string `json:"context"`
15+
}
16+
17+
if err := json.Unmarshal(req.Params, &params); err != nil {
18+
return NewInvalidParamsResponse(req.ID, "failed to parse params")
19+
}
20+
21+
if req.Ref == "" {
22+
return NewInvalidParamsResponse(req.ID, "ref field is required")
23+
}
24+
25+
// Get context from registry
26+
ctx, err := registry.GetContext(params.Context)
27+
if err != nil {
28+
return NewInvalidParamsResponse(req.ID, err.Error())
29+
}
30+
31+
// Create temp directory for chainstate data
32+
tempDir, err := os.MkdirTemp("", "btck_conformance_test_*")
33+
if err != nil {
34+
return NewEmptyErrorResponse(req.ID)
35+
}
36+
37+
dataDir := filepath.Join(tempDir, "data")
38+
blocksDir := filepath.Join(tempDir, "blocks")
39+
40+
// Create chainstate manager
41+
manager, err := kernel.NewChainstateManager(ctx, dataDir, blocksDir)
42+
if err != nil {
43+
_ = os.RemoveAll(tempDir)
44+
return NewEmptyErrorResponse(req.ID)
45+
}
46+
47+
registry.Store(req.Ref, &ChainstateManagerState{
48+
Manager: manager,
49+
TempDir: tempDir,
50+
})
51+
52+
return NewSuccessResponseWithRef(req.ID, req.Ref)
53+
}
54+
55+
// handleChainstateManagerGetActiveChain gets the active chain from a chainstate manager
56+
func handleChainstateManagerGetActiveChain(registry *Registry, req Request) Response {
57+
var params struct {
58+
ChainstateManager string `json:"chainstate_manager"`
59+
}
60+
61+
if err := json.Unmarshal(req.Params, &params); err != nil {
62+
return NewInvalidParamsResponse(req.ID, "failed to parse params")
63+
}
64+
65+
if req.Ref == "" {
66+
return NewInvalidParamsResponse(req.ID, "ref field is required")
67+
}
68+
69+
// Get chainstate manager from registry
70+
csm, err := registry.GetChainstateManager(params.ChainstateManager)
71+
if err != nil {
72+
return NewInvalidParamsResponse(req.ID, err.Error())
73+
}
74+
75+
// Get active chain
76+
chain := csm.Manager.GetActiveChain()
77+
78+
registry.Store(req.Ref, chain)
79+
80+
return NewSuccessResponseWithRef(req.ID, req.Ref)
81+
}
82+
83+
// handleChainstateManagerProcessBlock processes a block
84+
func handleChainstateManagerProcessBlock(registry *Registry, req Request) Response {
85+
var params struct {
86+
ChainstateManager string `json:"chainstate_manager"`
87+
Block string `json:"block"`
88+
}
89+
90+
if err := json.Unmarshal(req.Params, &params); err != nil {
91+
return NewInvalidParamsResponse(req.ID, "failed to parse params")
92+
}
93+
94+
// Get chainstate manager from registry
95+
csm, err := registry.GetChainstateManager(params.ChainstateManager)
96+
if err != nil {
97+
return NewInvalidParamsResponse(req.ID, err.Error())
98+
}
99+
100+
// Get block from registry
101+
block, err := registry.GetBlock(params.Block)
102+
if err != nil {
103+
return NewInvalidParamsResponse(req.ID, err.Error())
104+
}
105+
106+
// Process the block
107+
ok, newBlock := csm.Manager.ProcessBlock(block)
108+
if !ok {
109+
return NewEmptyErrorResponse(req.ID)
110+
}
111+
112+
// Return result with new_block field
113+
result := struct {
114+
NewBlock bool `json:"new_block"`
115+
}{
116+
NewBlock: newBlock,
117+
}
118+
return NewSuccessResponse(req.ID, result)
119+
}
120+
121+
// handleChainstateManagerDestroy destroys a chainstate manager
122+
func handleChainstateManagerDestroy(registry *Registry, req Request) Response {
123+
var params struct {
124+
ChainstateManager string `json:"chainstate_manager"`
125+
}
126+
127+
if err := json.Unmarshal(req.Params, &params); err != nil {
128+
return NewInvalidParamsResponse(req.ID, "failed to parse params")
129+
}
130+
131+
// Destroy and remove from registry
132+
if err := registry.Destroy(params.ChainstateManager); err != nil {
133+
return NewInvalidParamsResponse(req.ID, err.Error())
134+
}
135+
136+
return NewEmptySuccessResponse(req.ID)
137+
}

cmd/conformance-handler/context.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
6+
"github.com/stringintech/go-bitcoinkernel/kernel"
7+
)
8+
9+
// handleContextCreate creates a context with specified chain parameters
10+
func handleContextCreate(registry *Registry, req Request) Response {
11+
var params struct {
12+
ChainParameters struct {
13+
ChainType string `json:"chain_type"`
14+
} `json:"chain_parameters"`
15+
}
16+
17+
if err := json.Unmarshal(req.Params, &params); err != nil {
18+
return NewInvalidParamsResponse(req.ID, "failed to parse params")
19+
}
20+
21+
if req.Ref == "" {
22+
return NewInvalidParamsResponse(req.ID, "ref field is required")
23+
}
24+
25+
// Parse chain type
26+
var chainType kernel.ChainType
27+
switch params.ChainParameters.ChainType {
28+
case "btck_ChainType_MAINNET":
29+
chainType = kernel.ChainTypeMainnet
30+
case "btck_ChainType_TESTNET":
31+
chainType = kernel.ChainTypeTestnet
32+
case "btck_ChainType_TESTNET_4":
33+
chainType = kernel.ChainTypeTestnet4
34+
case "btck_ChainType_SIGNET":
35+
chainType = kernel.ChainTypeSignet
36+
case "btck_ChainType_REGTEST":
37+
chainType = kernel.ChainTypeRegtest
38+
default:
39+
return NewInvalidParamsResponse(req.ID, "unknown chain_type: "+params.ChainParameters.ChainType)
40+
}
41+
42+
// Create context
43+
ctx, err := kernel.NewContext(kernel.WithChainType(chainType))
44+
if err != nil {
45+
return NewEmptyErrorResponse(req.ID)
46+
}
47+
48+
registry.Store(req.Ref, ctx)
49+
50+
return NewSuccessResponseWithRef(req.ID, req.Ref)
51+
}
52+
53+
// handleContextDestroy destroys a context
54+
func handleContextDestroy(registry *Registry, req Request) Response {
55+
var params struct {
56+
Context string `json:"context"`
57+
}
58+
59+
if err := json.Unmarshal(req.Params, &params); err != nil {
60+
return NewInvalidParamsResponse(req.ID, "failed to parse params")
61+
}
62+
63+
// Destroy and remove from registry
64+
if err := registry.Destroy(params.Context); err != nil {
65+
return NewInvalidParamsResponse(req.ID, err.Error())
66+
}
67+
68+
return NewEmptySuccessResponse(req.ID)
69+
}

0 commit comments

Comments
 (0)