Skip to content

Commit 49eb8ae

Browse files
committed
Add Context, ContextOptions, and ChainParameters
1 parent 6cb91cc commit 49eb8ae

File tree

7 files changed

+414
-0
lines changed

7 files changed

+414
-0
lines changed

kernel/chain_parameters.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package kernel
2+
3+
/*
4+
#include "kernel/bitcoinkernel.h"
5+
*/
6+
import "C"
7+
import "runtime"
8+
9+
// ChainType represents the Bitcoin network type
10+
type ChainType int
11+
12+
const (
13+
ChainTypeMainnet ChainType = iota
14+
ChainTypeTestnet
15+
ChainTypeTestnet4
16+
ChainTypeSignet
17+
ChainTypeRegtest
18+
)
19+
20+
// ChainParameters wraps the C kernel_ChainParameters
21+
type ChainParameters struct {
22+
ptr *C.kernel_ChainParameters
23+
}
24+
25+
func NewChainParameters(chainType ChainType) (*ChainParameters, error) {
26+
var cChainType C.kernel_ChainType
27+
28+
switch chainType {
29+
case ChainTypeMainnet:
30+
cChainType = C.kernel_CHAIN_TYPE_MAINNET
31+
case ChainTypeTestnet:
32+
cChainType = C.kernel_CHAIN_TYPE_TESTNET
33+
case ChainTypeTestnet4:
34+
cChainType = C.kernel_CHAIN_TYPE_TESTNET_4
35+
case ChainTypeSignet:
36+
cChainType = C.kernel_CHAIN_TYPE_SIGNET
37+
case ChainTypeRegtest:
38+
cChainType = C.kernel_CHAIN_TYPE_REGTEST
39+
default:
40+
return nil, ErrInvalidChainType
41+
}
42+
43+
ptr := C.kernel_chain_parameters_create(cChainType)
44+
if ptr == nil {
45+
return nil, ErrChainParametersCreation
46+
}
47+
48+
cp := &ChainParameters{ptr: ptr}
49+
runtime.SetFinalizer(cp, (*ChainParameters).destroy)
50+
return cp, nil
51+
}
52+
53+
func (cp *ChainParameters) destroy() {
54+
if cp.ptr != nil {
55+
C.kernel_chain_parameters_destroy(cp.ptr)
56+
cp.ptr = nil
57+
}
58+
}
59+
60+
func (cp *ChainParameters) Destroy() {
61+
runtime.SetFinalizer(cp, nil)
62+
cp.destroy()
63+
}

kernel/chain_parameters_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package kernel
2+
3+
import (
4+
"errors"
5+
"testing"
6+
)
7+
8+
func TestNewChainParameters(t *testing.T) {
9+
tests := []struct {
10+
name string
11+
chainType ChainType
12+
wantErr bool
13+
errType error
14+
}{
15+
{
16+
name: "Mainnet chain parameters",
17+
chainType: ChainTypeMainnet,
18+
wantErr: false,
19+
},
20+
{
21+
name: "Testnet chain parameters",
22+
chainType: ChainTypeTestnet,
23+
wantErr: false,
24+
},
25+
{
26+
name: "Testnet4 chain parameters",
27+
chainType: ChainTypeTestnet4,
28+
wantErr: false,
29+
},
30+
{
31+
name: "Signet chain parameters",
32+
chainType: ChainTypeSignet,
33+
wantErr: false,
34+
},
35+
{
36+
name: "Regtest chain parameters",
37+
chainType: ChainTypeRegtest,
38+
wantErr: false,
39+
},
40+
{
41+
name: "Invalid chain type",
42+
chainType: ChainType(999),
43+
wantErr: true,
44+
errType: ErrInvalidChainType,
45+
},
46+
}
47+
48+
for _, tt := range tests {
49+
t.Run(tt.name, func(t *testing.T) {
50+
cp, err := NewChainParameters(tt.chainType)
51+
52+
if tt.wantErr {
53+
if err == nil {
54+
t.Errorf("NewChainParameters() error = nil, wantErr %v", tt.wantErr)
55+
return
56+
}
57+
if tt.errType != nil && !errors.Is(err, tt.errType) {
58+
t.Errorf("NewChainParameters() error = %v, want %v", err, tt.errType)
59+
}
60+
return
61+
}
62+
63+
if err != nil {
64+
t.Errorf("NewChainParameters() error = %v, wantErr %v", err, tt.wantErr)
65+
return
66+
}
67+
68+
if cp == nil {
69+
t.Error("NewChainParameters() returned nil ChainParameters")
70+
return
71+
}
72+
73+
if cp.ptr == nil {
74+
t.Error("ChainParameters has nil pointer")
75+
}
76+
77+
// Clean up
78+
cp.Destroy()
79+
})
80+
}
81+
}

kernel/context.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package kernel
2+
3+
/*
4+
#include "kernel/bitcoinkernel.h"
5+
*/
6+
import "C"
7+
import (
8+
"errors"
9+
"runtime"
10+
)
11+
12+
// Context wraps the C kernel_Context
13+
// Once other validation objects are instantiated from it, the context needs to be kept in
14+
// memory for the duration of their lifetimes.
15+
//
16+
// A constructed context can be safely used from multiple threads.
17+
type Context struct {
18+
ptr *C.kernel_Context
19+
}
20+
21+
// NewContext creates a new kernel context with the specified options.
22+
// Kernel copies all necessary data from the options during context creation,
23+
// so the caller can safely free the options object after this call returns.
24+
func NewContext(options *ContextOptions) (*Context, error) {
25+
if options == nil {
26+
return nil, errors.New("context options cannot be nil")
27+
}
28+
29+
ptr := C.kernel_context_create(options.ptr)
30+
if ptr == nil {
31+
return nil, ErrContextCreation
32+
}
33+
34+
ctx := &Context{ptr: ptr}
35+
runtime.SetFinalizer(ctx, (*Context).destroy)
36+
return ctx, nil
37+
}
38+
39+
// NewDefaultContext creates a new kernel context with default mainnet parameters.
40+
// The defer statements are safe here because the Kernel copies all necessary
41+
// data during context creation, so the caller can safely free the options and
42+
// parameters objects immediately after the context is created.
43+
func NewDefaultContext() (*Context, error) {
44+
opts, err := NewContextOptions()
45+
if err != nil {
46+
return nil, err
47+
}
48+
defer opts.Destroy()
49+
50+
params, err := NewChainParameters(ChainTypeMainnet)
51+
if err != nil {
52+
return nil, err
53+
}
54+
defer params.Destroy()
55+
56+
opts.SetChainParams(params)
57+
return NewContext(opts)
58+
}
59+
60+
// Interrupt can be used to halt long-running validation functions
61+
func (ctx *Context) Interrupt() bool {
62+
if ctx.ptr == nil {
63+
return false
64+
}
65+
return bool(C.kernel_context_interrupt(ctx.ptr))
66+
}
67+
68+
func (ctx *Context) destroy() {
69+
if ctx.ptr != nil {
70+
C.kernel_context_destroy(ctx.ptr)
71+
ctx.ptr = nil
72+
}
73+
}
74+
75+
func (ctx *Context) Destroy() {
76+
runtime.SetFinalizer(ctx, nil)
77+
ctx.destroy()
78+
}

kernel/context_options.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package kernel
2+
3+
/*
4+
#include "kernel/bitcoinkernel.h"
5+
*/
6+
import "C"
7+
import (
8+
"runtime"
9+
)
10+
11+
// ContextOptions wraps the C kernel_ContextOptions
12+
type ContextOptions struct {
13+
ptr *C.kernel_ContextOptions
14+
}
15+
16+
func NewContextOptions() (*ContextOptions, error) {
17+
ptr := C.kernel_context_options_create()
18+
if ptr == nil {
19+
return nil, ErrContextOptionsCreation
20+
}
21+
22+
opts := &ContextOptions{ptr: ptr}
23+
runtime.SetFinalizer(opts, (*ContextOptions).destroy)
24+
return opts, nil
25+
}
26+
27+
// SetChainParams sets the chain parameters for these context options.
28+
// Kernel makes a copy of the chain parameters, so the caller can
29+
// safely free the chainParams object after this call returns.
30+
func (opts *ContextOptions) SetChainParams(chainParams *ChainParameters) {
31+
if opts.ptr == nil {
32+
panic("ContextOptions pointer is nil")
33+
}
34+
if chainParams.ptr == nil {
35+
panic("ChainParameters pointer is nil")
36+
}
37+
C.kernel_context_options_set_chainparams(opts.ptr, chainParams.ptr)
38+
}
39+
40+
func (opts *ContextOptions) destroy() {
41+
if opts.ptr != nil {
42+
C.kernel_context_options_destroy(opts.ptr)
43+
opts.ptr = nil
44+
}
45+
}
46+
47+
func (opts *ContextOptions) Destroy() {
48+
runtime.SetFinalizer(opts, nil)
49+
opts.destroy()
50+
}

kernel/context_test.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package kernel
2+
3+
import (
4+
"errors"
5+
"testing"
6+
)
7+
8+
func TestNewContext(t *testing.T) {
9+
tests := []struct {
10+
name string
11+
setupOption func() *ContextOptions
12+
wantErr bool
13+
errType error
14+
}{
15+
{
16+
name: "Valid context options",
17+
setupOption: func() *ContextOptions {
18+
opts, err := NewContextOptions()
19+
if err != nil {
20+
t.Fatalf("Failed to create context options: %v", err)
21+
}
22+
params, err := NewChainParameters(ChainTypeMainnet)
23+
if err != nil {
24+
t.Fatalf("Failed to create chain parameters: %v", err)
25+
}
26+
defer params.Destroy()
27+
opts.SetChainParams(params)
28+
return opts
29+
},
30+
wantErr: false,
31+
},
32+
{
33+
name: "Nil context options",
34+
setupOption: func() *ContextOptions {
35+
return nil
36+
},
37+
wantErr: true,
38+
errType: errors.New("context options cannot be nil"),
39+
},
40+
}
41+
42+
for _, tt := range tests {
43+
t.Run(tt.name, func(t *testing.T) {
44+
opts := tt.setupOption()
45+
if opts != nil {
46+
defer opts.Destroy()
47+
}
48+
49+
ctx, err := NewContext(opts)
50+
51+
if tt.wantErr {
52+
if err == nil {
53+
t.Errorf("NewContext() error = nil, wantErr %v", tt.wantErr)
54+
return
55+
}
56+
if tt.errType != nil && err.Error() != tt.errType.Error() {
57+
t.Errorf("NewContext() error = %v, want %v", err, tt.errType)
58+
}
59+
return
60+
}
61+
62+
if err != nil {
63+
t.Errorf("NewContext() error = %v, wantErr %v", err, tt.wantErr)
64+
return
65+
}
66+
67+
if ctx == nil {
68+
t.Error("NewContext() returned nil Context")
69+
return
70+
}
71+
72+
if ctx.ptr == nil {
73+
t.Error("Context has nil pointer")
74+
}
75+
76+
// Clean up
77+
ctx.Destroy()
78+
})
79+
}
80+
}
81+
82+
func TestNewDefaultContext(t *testing.T) {
83+
ctx, err := NewDefaultContext()
84+
85+
if err != nil {
86+
t.Errorf("NewDefaultContext() error = %v, want nil", err)
87+
return
88+
}
89+
90+
if ctx == nil {
91+
t.Error("NewDefaultContext() returned nil Context")
92+
return
93+
}
94+
95+
if ctx.ptr == nil {
96+
t.Error("Context has nil pointer")
97+
}
98+
}
99+
100+
func TestContextInterrupt(t *testing.T) {
101+
ctx, err := NewDefaultContext()
102+
if err != nil {
103+
t.Fatalf("Failed to create default context: %v", err)
104+
}
105+
defer ctx.Destroy()
106+
107+
// Test interrupt on valid context
108+
result := ctx.Interrupt()
109+
// Result can be true or false, both could be valid
110+
t.Logf("Context interrupt result: %v", result)
111+
112+
// Destroy and test interrupt on destroyed context
113+
ctx.Destroy()
114+
result = ctx.Interrupt()
115+
if result {
116+
t.Error("Interrupt() should return false after context is destroyed")
117+
}
118+
}

0 commit comments

Comments
 (0)