Skip to content

Commit 16ac1b8

Browse files
committed
Add writer helper, adapt to C API changes, and add new tests
1 parent 7b9e22e commit 16ac1b8

33 files changed

+1449
-491
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ The library uses structured error types for better error handling (see [errors.g
101101

102102
```go
103103
m := kernel.ChainstateManager{}
104-
blockIndex := m.GetBlockIndexFromGenesis() // panic: chainstateManager is not initialized
104+
chain, _ := m.GetActiveChain() // panic: chainstateManager is not initialized
105105
```
106106

107107
Always ensure objects are properly initialized (and not destroyed) before calling methods on them. Constructor functions like `NewChainstateManager()` return errors for validation, but method calls on receivers expect valid objects.

kernel/block.go

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,24 @@ import (
1111

1212
var _ cManagedResource = &Block{}
1313

14-
// Block wraps the C kernel_Block
14+
// Block wraps the C btck_Block
1515
type Block struct {
16-
ptr *C.kernel_Block
16+
ptr *C.btck_Block
1717
}
1818

1919
// NewBlockFromRaw creates a new block from raw serialized data
2020
func NewBlockFromRaw(rawBlock []byte) (*Block, error) {
2121
if len(rawBlock) == 0 {
2222
return nil, ErrEmptyBlockData
2323
}
24-
ptr := C.kernel_block_create((*C.uchar)(unsafe.Pointer(&rawBlock[0])), C.size_t(len(rawBlock)))
24+
ptr := C.btck_block_create(unsafe.Pointer(&rawBlock[0]), C.size_t(len(rawBlock)))
2525
if ptr == nil {
2626
return nil, ErrKernelBlockCreate
2727
}
2828
return newBlockFromPtr(ptr), nil
2929
}
3030

31-
func newBlockFromPtr(ptr *C.kernel_Block) *Block {
31+
func newBlockFromPtr(ptr *C.btck_Block) *Block {
3232
block := &Block{ptr: ptr}
3333
runtime.SetFinalizer(block, (*Block).destroy)
3434
return block
@@ -37,7 +37,7 @@ func newBlockFromPtr(ptr *C.kernel_Block) *Block {
3737
func (b *Block) Hash() (*BlockHash, error) {
3838
checkReady(b)
3939

40-
ptr := C.kernel_block_get_hash(b.ptr)
40+
ptr := C.btck_block_get_hash(b.ptr)
4141
if ptr == nil {
4242
return nil, ErrKernelBlockGetHash
4343
}
@@ -51,25 +51,49 @@ func (b *Block) Hash() (*BlockHash, error) {
5151
func (b *Block) Data() ([]byte, error) {
5252
checkReady(b)
5353

54-
byteArray := C.kernel_block_copy_data(b.ptr)
55-
if byteArray == nil {
56-
return nil, ErrKernelCopyBlockData
54+
// Use the callback helper to collect bytes from btck_block_to_bytes
55+
return writeToBytes(func(writer C.btck_WriteBytes, userData unsafe.Pointer) C.int {
56+
return C.btck_block_to_bytes(b.ptr, writer, userData)
57+
})
58+
}
59+
60+
// Copy creates a copy of the block
61+
func (b *Block) Copy() (*Block, error) {
62+
checkReady(b)
63+
64+
ptr := C.btck_block_copy(b.ptr)
65+
if ptr == nil {
66+
return nil, ErrKernelBlockCopy
5767
}
58-
defer C.kernel_byte_array_destroy(byteArray)
5968

60-
size := int(byteArray.size)
61-
if size == 0 {
62-
return nil, nil
69+
return newBlockFromPtr(ptr), nil
70+
}
71+
72+
// CountTransactions returns the number of transactions in the block
73+
func (b *Block) CountTransactions() (uint64, error) {
74+
checkReady(b)
75+
76+
count := C.btck_block_count_transactions(b.ptr)
77+
return uint64(count), nil
78+
}
79+
80+
// GetTransactionAt returns the transaction at the specified index.
81+
func (b *Block) GetTransactionAt(index uint64) (*Transaction, error) {
82+
checkReady(b)
83+
84+
ptr := C.btck_block_get_transaction_at(b.ptr, C.uint64_t(index))
85+
if ptr == nil {
86+
return nil, ErrKernelBlockGetTransaction
6387
}
6488

65-
// Copy the data to Go slice
66-
data := C.GoBytes(unsafe.Pointer(byteArray.data), C.int(size))
67-
return data, nil
89+
transaction := &Transaction{ptr: ptr}
90+
runtime.SetFinalizer(transaction, (*Transaction).destroy)
91+
return transaction, nil
6892
}
6993

7094
func (b *Block) destroy() {
7195
if b.ptr != nil {
72-
C.kernel_block_destroy(b.ptr)
96+
C.btck_block_destroy(b.ptr)
7397
b.ptr = nil
7498
}
7599
}

kernel/block_hash.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ import (
1111

1212
var _ cManagedResource = &BlockHash{}
1313

14-
// BlockHash wraps the C kernel_BlockHash
14+
// BlockHash wraps the C btck_BlockHash
1515
type BlockHash struct {
16-
ptr *C.kernel_BlockHash
16+
ptr *C.btck_BlockHash
1717
}
1818

1919
// Bytes returns the raw hash bytes
@@ -25,7 +25,7 @@ func (bh *BlockHash) Bytes() []byte {
2525

2626
func (bh *BlockHash) destroy() {
2727
if bh.ptr != nil {
28-
C.kernel_block_hash_destroy(bh.ptr)
28+
C.btck_block_hash_destroy(bh.ptr)
2929
bh.ptr = nil
3030
}
3131
}

kernel/block_index.go

Lines changed: 0 additions & 67 deletions
This file was deleted.

kernel/block_spent_outputs.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package kernel
2+
3+
/*
4+
#include "kernel/bitcoinkernel.h"
5+
*/
6+
import "C"
7+
import (
8+
"runtime"
9+
)
10+
11+
var _ cManagedResource = &BlockSpentOutputs{}
12+
13+
// BlockSpentOutputs wraps the C btck_BlockSpentOutputs
14+
type BlockSpentOutputs struct {
15+
ptr *C.btck_BlockSpentOutputs
16+
}
17+
18+
// Size returns the number of transaction spent outputs contained in block spent outputs
19+
func (bso *BlockSpentOutputs) Size() uint64 {
20+
checkReady(bso)
21+
return uint64(C.btck_block_spent_outputs_size(bso.ptr))
22+
}
23+
24+
// GetTransactionSpentOutputsAt returns the transaction spent outputs at the specified index
25+
func (bso *BlockSpentOutputs) GetTransactionSpentOutputsAt(index uint64) (*TransactionSpentOutputs, error) {
26+
checkReady(bso)
27+
ptr := C.btck_block_spent_outputs_get_transaction_spent_outputs_at(bso.ptr, C.uint64_t(index))
28+
if ptr == nil {
29+
return nil, ErrBlockSpentOutputsGetTransactionSpentOutputsAt
30+
}
31+
32+
txSpentOutputs := &TransactionSpentOutputs{ptr: ptr}
33+
runtime.SetFinalizer(txSpentOutputs, (*TransactionSpentOutputs).destroy)
34+
return txSpentOutputs, nil
35+
}
36+
37+
// Copy creates a copy of the block spent outputs
38+
func (bso *BlockSpentOutputs) Copy() (*BlockSpentOutputs, error) {
39+
checkReady(bso)
40+
41+
ptr := C.btck_block_spent_outputs_copy(bso.ptr)
42+
if ptr == nil {
43+
return nil, ErrKernelBlockSpentOutputsCopy
44+
}
45+
46+
outputs := &BlockSpentOutputs{ptr: ptr}
47+
runtime.SetFinalizer(outputs, (*BlockSpentOutputs).destroy)
48+
return outputs, nil
49+
}
50+
51+
func (bso *BlockSpentOutputs) destroy() {
52+
if bso.ptr != nil {
53+
C.btck_block_spent_outputs_destroy(bso.ptr)
54+
bso.ptr = nil
55+
}
56+
}
57+
58+
func (bso *BlockSpentOutputs) Destroy() {
59+
runtime.SetFinalizer(bso, nil)
60+
bso.destroy()
61+
}
62+
63+
func (bso *BlockSpentOutputs) isReady() bool {
64+
return bso != nil && bso.ptr != nil
65+
}
66+
67+
func (bso *BlockSpentOutputs) uninitializedError() error {
68+
return ErrBlockSpentOutputsUninitialized
69+
}

kernel/block_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,82 @@ func TestBlockFromRaw(t *testing.T) {
6969
t.Logf("Expected data hex: %s, got %s", genesisHex, hexStr)
7070
}
7171
}
72+
73+
func TestBlockCopy(t *testing.T) {
74+
genesisHex := "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000"
75+
genesisBytes, err := hex.DecodeString(genesisHex)
76+
if err != nil {
77+
t.Fatalf("Failed to decode genesis hex: %v", err)
78+
}
79+
80+
block, err := NewBlockFromRaw(genesisBytes)
81+
if err != nil {
82+
t.Fatalf("NewBlockFromRaw() error = %v", err)
83+
}
84+
defer block.Destroy()
85+
86+
// Test copying block
87+
blockCopy, err := block.Copy()
88+
if err != nil {
89+
t.Fatalf("Block.Copy() error = %v", err)
90+
}
91+
if blockCopy == nil {
92+
t.Fatal("Copied block is nil")
93+
}
94+
defer blockCopy.Destroy()
95+
96+
if blockCopy.ptr == nil {
97+
t.Error("Copied block pointer is nil")
98+
}
99+
}
100+
101+
func TestBlockCountTransactions(t *testing.T) {
102+
genesisHex := "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000"
103+
genesisBytes, err := hex.DecodeString(genesisHex)
104+
if err != nil {
105+
t.Fatalf("Failed to decode genesis hex: %v", err)
106+
}
107+
108+
block, err := NewBlockFromRaw(genesisBytes)
109+
if err != nil {
110+
t.Fatalf("NewBlockFromRaw() error = %v", err)
111+
}
112+
defer block.Destroy()
113+
114+
// Test counting transactions (genesis block has 1 transaction)
115+
txCount, err := block.CountTransactions()
116+
if err != nil {
117+
t.Fatalf("Block.CountTransactions() error = %v", err)
118+
}
119+
if txCount != 1 {
120+
t.Errorf("Expected 1 transaction, got %d", txCount)
121+
}
122+
}
123+
124+
func TestBlockGetTransactionAt(t *testing.T) {
125+
genesisHex := "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000"
126+
genesisBytes, err := hex.DecodeString(genesisHex)
127+
if err != nil {
128+
t.Fatalf("Failed to decode genesis hex: %v", err)
129+
}
130+
131+
block, err := NewBlockFromRaw(genesisBytes)
132+
if err != nil {
133+
t.Fatalf("NewBlockFromRaw() error = %v", err)
134+
}
135+
defer block.Destroy()
136+
137+
// Test getting transaction at index 0
138+
tx, err := block.GetTransactionAt(0)
139+
if err != nil {
140+
t.Fatalf("Block.GetTransactionAt(0) error = %v", err)
141+
}
142+
if tx == nil {
143+
t.Fatal("Transaction is nil")
144+
}
145+
defer tx.Destroy()
146+
147+
if tx.ptr == nil {
148+
t.Error("Transaction pointer is nil")
149+
}
150+
}

0 commit comments

Comments
 (0)