Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 38 additions & 3 deletions kernel/chainstate_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,47 @@ func newChainstateManager(ptr *C.btck_ChainstateManager) *ChainstateManager {
// This is the main object for validation tasks, retrieving data from the chain, and
// interacting with chainstate and indexes.
//
// The chainstate manager associates with the provided kernel context and uses the specified
// data and block directories. If the directories do not exist, they will be created.
//
// Usage:
//
// chainman, err := NewChainstateManager(ctx, dataDir, blocksDir,
// WithWorkerThreads(1),
// WithBlockTreeDBInMemory,
// )
//
// Parameters:
// - options: Configuration options created by NewChainstateManagerOptions
// - context: Kernel context that the chainstate manager will associate with
// - dataDir: Path to the directory containing chainstate data
// - blocksDir: Path to the directory containing block data
// - options: Zero or more ChainstateManagerOption functional options
//
// Returns an error if the chainstate manager cannot be created.
func NewChainstateManager(options *ChainstateManagerOptions) (*ChainstateManager, error) {
ptr := C.btck_chainstate_manager_create((*C.btck_ChainstateManagerOptions)(options.ptr))
func NewChainstateManager(context *Context, dataDir, blocksDir string, options ...ChainstateManagerOption) (*ChainstateManager, error) {
cDataDir := C.CString(dataDir)
defer C.free(unsafe.Pointer(cDataDir))

cBlocksDir := C.CString(blocksDir)
defer C.free(unsafe.Pointer(cBlocksDir))

// Create the options
optsPtr := C.btck_chainstate_manager_options_create((*C.btck_Context)(context.ptr), cDataDir, C.size_t(len(dataDir)),
cBlocksDir, C.size_t(len(blocksDir)))
if optsPtr == nil {
return nil, &InternalError{"Failed to create chainstate manager options"}
}
defer C.btck_chainstate_manager_options_destroy(optsPtr)

// Apply all functional options
for _, opt := range options {
if err := opt(optsPtr); err != nil {
return nil, err
}
}

// Create the chainstate manager
ptr := C.btck_chainstate_manager_create(optsPtr)
if ptr == nil {
return nil, &InternalError{"Failed to create chainstate manager"}
}
Expand Down
119 changes: 34 additions & 85 deletions kernel/chainstate_manager_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,67 +2,24 @@ package kernel

/*
#include "bitcoinkernel.h"
#include <stdlib.h>
*/
import "C"
import (
"unsafe"
)

type chainstateManagerOptionsCFuncs struct{}
// ChainstateManagerOption is a functional option for configuring chainstate manager.
type ChainstateManagerOption func(*C.btck_ChainstateManagerOptions) error

func (chainstateManagerOptionsCFuncs) destroy(ptr unsafe.Pointer) {
C.btck_chainstate_manager_options_destroy((*C.btck_ChainstateManagerOptions)(ptr))
}

// ChainstateManagerOptions holds configuration options for creating a new chainstate manager.
//
// Options are initialized with sensible defaults and can be customized using the
// setter methods before creating the chainstate manager.
type ChainstateManagerOptions struct {
*uniqueHandle
}

func newChainstateManagerOptions(ptr *C.btck_ChainstateManagerOptions) *ChainstateManagerOptions {
h := newUniqueHandle(unsafe.Pointer(ptr), chainstateManagerOptionsCFuncs{})
return &ChainstateManagerOptions{uniqueHandle: h}
}

// NewChainstateManagerOptions creates options for configuring a chainstate manager.
//
// The options associate with the provided kernel context and specify the data and block
// directories. If the directories do not exist, they will be created.
//
// Parameters:
// - context: Kernel context that the chainstate manager will associate with
// - dataDir: Path to the directory containing chainstate data
// - blocksDir: Path to the directory containing block data
//
// Returns an error if the options cannot be created.
func NewChainstateManagerOptions(context *Context, dataDir, blocksDir string) (*ChainstateManagerOptions, error) {
cDataDir := C.CString(dataDir)
defer C.free(unsafe.Pointer(cDataDir))

cBlocksDir := C.CString(blocksDir)
defer C.free(unsafe.Pointer(cBlocksDir))

ptr := C.btck_chainstate_manager_options_create((*C.btck_Context)(context.ptr), cDataDir, C.size_t(len(dataDir)),
cBlocksDir, C.size_t(len(blocksDir)))
if ptr == nil {
return nil, &InternalError{"Failed to create chainstate manager options"}
}
return newChainstateManagerOptions(ptr), nil
}

// SetWorkerThreads configures the number of worker threads for parallel validation.
// WithWorkerThreads returns a ChainstateManagerOption that configures the number of worker threads for parallel validation.
//
// Parameters:
// - threads: Number of worker threads (0 disables parallel verification, max is clamped to 15)
func (opts *ChainstateManagerOptions) SetWorkerThreads(threads int) {
C.btck_chainstate_manager_options_set_worker_threads_num((*C.btck_ChainstateManagerOptions)(opts.ptr), C.int(threads))
func WithWorkerThreads(threads int) ChainstateManagerOption {
return func(opts *C.btck_ChainstateManagerOptions) error {
C.btck_chainstate_manager_options_set_worker_threads_num(opts, C.int(threads))
return nil
}
}

// SetWipeDBs configures which databases to wipe on startup.
// WithWipeDBs returns a ChainstateManagerOption that configures which databases to wipe on startup.
//
// When combined with ImportBlocks, this triggers a full reindex (if wipeBlockTree is true)
// or chainstate-only reindex (if only wipeChainstate is true).
Expand All @@ -72,42 +29,34 @@ func (opts *ChainstateManagerOptions) SetWorkerThreads(threads int) {
// - wipeChainstate: Whether to wipe the chainstate database
//
// Returns an error if wipeBlockTree is true but wipeChainstate is false.
func (opts *ChainstateManagerOptions) SetWipeDBs(wipeBlockTree, wipeChainstate bool) error {
wipeBlockTreeInt := 0
if wipeBlockTree {
wipeBlockTreeInt = 1
}
wipeChainstateInt := 0
if wipeChainstate {
wipeChainstateInt = 1
func WithWipeDBs(wipeBlockTree, wipeChainstate bool) ChainstateManagerOption {
return func(opts *C.btck_ChainstateManagerOptions) error {
wipeBlockTreeInt := 0
if wipeBlockTree {
wipeBlockTreeInt = 1
}
wipeChainstateInt := 0
if wipeChainstate {
wipeChainstateInt = 1
}
result := C.btck_chainstate_manager_options_set_wipe_dbs(opts, C.int(wipeBlockTreeInt), C.int(wipeChainstateInt))
if result != 0 {
return &InternalError{"Failed to set wipe db"}
}
return nil
}
result := C.btck_chainstate_manager_options_set_wipe_dbs((*C.btck_ChainstateManagerOptions)(opts.ptr), C.int(wipeBlockTreeInt), C.int(wipeChainstateInt))
if result != 0 {
return &InternalError{"Failed to set wipe db"}
}
return nil
}

// UpdateBlockTreeDBInMemory configures whether the block tree database is stored in memory.
//
// Parameters:
// - inMemory: If true, the block tree database will be kept entirely in memory
func (opts *ChainstateManagerOptions) UpdateBlockTreeDBInMemory(inMemory bool) {
inMemoryInt := 0
if inMemory {
inMemoryInt = 1
}
C.btck_chainstate_manager_options_update_block_tree_db_in_memory((*C.btck_ChainstateManagerOptions)(opts.ptr), C.int(inMemoryInt))
// WithBlockTreeDBInMemory is a ChainstateManagerOption that configures
// the block tree database to be stored in memory.
var WithBlockTreeDBInMemory ChainstateManagerOption = func(opts *C.btck_ChainstateManagerOptions) error {
C.btck_chainstate_manager_options_update_block_tree_db_in_memory(opts, C.int(1))
return nil
}

// UpdateChainstateDBInMemory configures whether the chainstate database is stored in memory.
//
// Parameters:
// - inMemory: If true, the chainstate database will be kept entirely in memory
func (opts *ChainstateManagerOptions) UpdateChainstateDBInMemory(inMemory bool) {
inMemoryInt := 0
if inMemory {
inMemoryInt = 1
}
C.btck_chainstate_manager_options_update_chainstate_db_in_memory((*C.btck_ChainstateManagerOptions)(opts.ptr), C.int(inMemoryInt))
// WithChainstateDBInMemory is a ChainstateManagerOption that configures
// the chainstate database to be stored in memory.
var WithChainstateDBInMemory ChainstateManagerOption = func(opts *C.btck_ChainstateManagerOptions) error {
C.btck_chainstate_manager_options_update_chainstate_db_in_memory(opts, C.int(1))
return nil
}
40 changes: 11 additions & 29 deletions kernel/chainstate_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,47 +152,29 @@ func (s *ChainstateManagerTestSuite) Setup(t *testing.T) {
dataDir := filepath.Join(tempDir, "data")
blocksDir := filepath.Join(tempDir, "blocks")

contextOpts := NewContextOptions()

chainParams, err := NewChainParameters(ChainTypeRegtest)
if err != nil {
t.Fatalf("NewChainParameters() error = %v", err)
}
t.Cleanup(func() { chainParams.Destroy() })

contextOpts.SetChainParams(chainParams)
var contextOpts []ContextOption
contextOpts = append(contextOpts, WithChainType(ChainTypeRegtest))

if s.NotificationCallbacks != nil {
contextOpts.SetNotifications(s.NotificationCallbacks)
contextOpts = append(contextOpts, WithNotifications(s.NotificationCallbacks))
}

if s.ValidationCallbacks != nil {
contextOpts.SetValidationInterface(s.ValidationCallbacks)
contextOpts = append(contextOpts, WithValidationInterface(s.ValidationCallbacks))
}

ctx, err := NewContext(contextOpts)
ctx, err := NewContext(contextOpts...)
if err != nil {
t.Fatalf("NewContext() error = %v", err)
}
t.Cleanup(func() { ctx.Destroy() })

opts, err := NewChainstateManagerOptions(ctx, dataDir, blocksDir)
if err != nil {
t.Fatalf("NewChainstateManagerOptions() error = %v", err)
}
t.Cleanup(func() { opts.Destroy() })

opts.SetWorkerThreads(1)
opts.UpdateBlockTreeDBInMemory(true)
opts.UpdateChainstateDBInMemory(true)
// Wipe both databases to enable proper initialization
err = opts.SetWipeDBs(true, true)
if err != nil {
t.Fatalf("SetWipeDBs() error = %v", err)
}

// Create chainstate manager
manager, err := NewChainstateManager(opts)
manager, err := NewChainstateManager(ctx, dataDir, blocksDir,
WithWorkerThreads(1),
WithBlockTreeDBInMemory,
WithChainstateDBInMemory,
WithWipeDBs(true, true),
)
if err != nil {
t.Fatalf("NewChainstateManager() error = %v", err)
}
Expand Down
35 changes: 30 additions & 5 deletions kernel/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,38 @@ func newContext(ptr *C.btck_Context, fromOwned bool) *Context {

// NewContext creates a new kernel context.
//
// The context holds chain-specific parameters and callbacks for handling error and
// validation events. If no options are provided, the context assumes mainnet chain
// parameters and no callbacks.
//
// Usage:
//
// ctx, err := NewContext(
// WithChainType(ChainTypeRegtest),
// WithNotifications(notificationCallbacks),
// )
//
// Parameters:
// - options: Context configuration created by NewContextOptions (can be nil for defaults)
// - options: Zero or more ContextOption functional options
//
// Returns an error if the context cannot be created. If options is nil or not configured,
// the context assumes mainnet chain parameters and no callbacks.
func NewContext(options *ContextOptions) (*Context, error) {
ptr := C.btck_context_create((*C.btck_ContextOptions)(options.ptr))
// Returns an error if the context cannot be created.
func NewContext(options ...ContextOption) (*Context, error) {
// Create the options
optsPtr := C.btck_context_options_create()
if optsPtr == nil {
return nil, &InternalError{"Failed to create context options"}
}
defer C.btck_context_options_destroy(optsPtr)

// Apply all functional options
for _, opt := range options {
if err := opt(optsPtr); err != nil {
return nil, err
}
}

// Create the context
ptr := C.btck_context_create(optsPtr)
if ptr == nil {
return nil, &InternalError{"Failed to create context"}
}
Expand Down
Loading
Loading