Skip to content

Commit 18cdf84

Browse files
committed
Implement conformance handler for v0.0.3 tests
1 parent aba26e4 commit 18cdf84

12 files changed

Lines changed: 896 additions & 97 deletions

File tree

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.4
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.4`
3333
- Test Repository: [stringintech/kernel-bindings-tests](https://github.com/stringintech/kernel-bindings-tests)

cmd/conformance-handler/block.go

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

cmd/conformance-handler/chain.go

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

cmd/conformance-handler/context.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
7+
"github.com/stringintech/go-bitcoinkernel/kernel"
8+
)
9+
10+
// handleContextCreate creates a context with specified chain parameters
11+
func handleContextCreate(registry *Registry, req Request) (Response, error) {
12+
var params struct {
13+
ChainParameters struct {
14+
ChainType string `json:"chain_type"`
15+
} `json:"chain_parameters"`
16+
}
17+
18+
if err := json.Unmarshal(req.Params, &params); err != nil {
19+
return Response{}, fmt.Errorf("failed to parse params: %w", err)
20+
}
21+
22+
if req.Ref == "" {
23+
return Response{}, fmt.Errorf("ref field is required")
24+
}
25+
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 Response{}, fmt.Errorf("unknown chain_type: %s", params.ChainParameters.ChainType)
40+
}
41+
42+
ctx, err := kernel.NewContext(kernel.WithChainType(chainType))
43+
if err != nil {
44+
return NewEmptyErrorResponse(req.ID), nil
45+
}
46+
47+
registry.Store(req.Ref, ctx)
48+
49+
return NewSuccessResponseWithRef(req.ID, req.Ref), nil
50+
}
51+
52+
// handleContextDestroy destroys a context
53+
func handleContextDestroy(registry *Registry, req Request) (Response, error) {
54+
var params struct {
55+
Context RefObject `json:"context"`
56+
}
57+
58+
if err := json.Unmarshal(req.Params, &params); err != nil {
59+
return Response{}, fmt.Errorf("failed to parse params: %w", err)
60+
}
61+
62+
if err := registry.Destroy(params.Context.Ref); err != nil {
63+
return Response{}, err
64+
}
65+
66+
return NewEmptySuccessResponse(req.ID), nil
67+
}

0 commit comments

Comments
 (0)