Skip to content

Commit b634adf

Browse files
committed
Add Block and BlockHash
1 parent 49eb8ae commit b634adf

File tree

5 files changed

+202
-0
lines changed

5 files changed

+202
-0
lines changed

kernel/block.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package kernel
2+
3+
/*
4+
#include "kernel/bitcoinkernel.h"
5+
*/
6+
import "C"
7+
import (
8+
"runtime"
9+
"unsafe"
10+
)
11+
12+
// Block wraps the C kernel_Block
13+
type Block struct {
14+
ptr *C.kernel_Block
15+
}
16+
17+
// NewBlockFromRaw creates a new block from raw serialized data
18+
func NewBlockFromRaw(rawBlock []byte) (*Block, error) {
19+
if len(rawBlock) == 0 {
20+
return nil, ErrInvalidBlockData
21+
}
22+
23+
ptr := C.kernel_block_create((*C.uchar)(unsafe.Pointer(&rawBlock[0])), C.size_t(len(rawBlock)))
24+
if ptr == nil {
25+
return nil, ErrBlockCreation
26+
}
27+
28+
block := &Block{ptr: ptr}
29+
runtime.SetFinalizer(block, (*Block).destroy)
30+
return block, nil
31+
}
32+
33+
func (b *Block) Hash() (*BlockHash, error) {
34+
if b.ptr == nil {
35+
return nil, ErrInvalidBlock
36+
}
37+
38+
ptr := C.kernel_block_get_hash(b.ptr)
39+
if ptr == nil {
40+
return nil, ErrHashCalculation
41+
}
42+
43+
hash := &BlockHash{ptr: ptr}
44+
runtime.SetFinalizer(hash, (*BlockHash).destroy)
45+
return hash, nil
46+
}
47+
48+
// Data returns the serialized block data
49+
func (b *Block) Data() ([]byte, error) {
50+
if b.ptr == nil {
51+
return nil, ErrInvalidBlock
52+
}
53+
54+
byteArray := C.kernel_copy_block_data(b.ptr)
55+
if byteArray == nil {
56+
return nil, ErrBlockDataCopy
57+
}
58+
defer C.kernel_byte_array_destroy(byteArray)
59+
60+
size := int(byteArray.size)
61+
if size == 0 {
62+
return nil, nil
63+
}
64+
65+
// Copy the data to Go slice
66+
data := C.GoBytes(unsafe.Pointer(byteArray.data), C.int(size))
67+
return data, nil
68+
}
69+
70+
func (b *Block) destroy() {
71+
if b.ptr != nil {
72+
C.kernel_block_destroy(b.ptr)
73+
b.ptr = nil
74+
}
75+
}
76+
77+
func (b *Block) Destroy() {
78+
runtime.SetFinalizer(b, nil)
79+
b.destroy()
80+
}

kernel/block_hash.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package kernel
2+
3+
/*
4+
#include "kernel/bitcoinkernel.h"
5+
*/
6+
import "C"
7+
import (
8+
"runtime"
9+
"unsafe"
10+
)
11+
12+
// BlockHash wraps the C kernel_BlockHash
13+
type BlockHash struct {
14+
ptr *C.kernel_BlockHash
15+
}
16+
17+
func (bh *BlockHash) destroy() {
18+
if bh.ptr != nil {
19+
C.kernel_block_hash_destroy(bh.ptr)
20+
bh.ptr = nil
21+
}
22+
}
23+
24+
func (bh *BlockHash) Destroy() {
25+
runtime.SetFinalizer(bh, nil)
26+
bh.destroy()
27+
}
28+
29+
// Bytes returns the raw hash bytes
30+
func (bh *BlockHash) Bytes() []byte {
31+
if bh.ptr == nil {
32+
return nil
33+
}
34+
// BlockHash is a 32-byte array in the C struct
35+
return C.GoBytes(unsafe.Pointer(&bh.ptr.hash[0]), 32)
36+
}

kernel/block_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package kernel
2+
3+
import (
4+
"encoding/hex"
5+
"errors"
6+
"testing"
7+
)
8+
9+
func TestInvalidBlockData(t *testing.T) {
10+
// Test with empty data
11+
_, err := NewBlockFromRaw([]byte{})
12+
if !errors.Is(err, ErrInvalidBlockData) {
13+
t.Errorf("Expected ErrInvalidBlockData, got %v", err)
14+
}
15+
16+
// Test with invalid data
17+
_, err = NewBlockFromRaw([]byte{0x00, 0x01, 0x02})
18+
if !errors.Is(err, ErrBlockCreation) {
19+
t.Errorf("Expected ErrBlockCreation, got %v", err)
20+
}
21+
}
22+
23+
func TestBlockFromRaw(t *testing.T) {
24+
// Complete Bitcoin mainnet genesis block (285 bytes)
25+
genesisHex := "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000"
26+
genesisBytes, err := hex.DecodeString(genesisHex)
27+
if err != nil {
28+
t.Fatalf("Failed to decode genesis hex: %v", err)
29+
}
30+
31+
block, err := NewBlockFromRaw(genesisBytes)
32+
if err != nil {
33+
t.Fatalf("NewBlockFromRaw() error = %v", err)
34+
}
35+
defer block.Destroy()
36+
37+
// Test getting block hash
38+
hash, err := block.Hash()
39+
if err != nil {
40+
t.Fatalf("Block.Hash() error = %v", err)
41+
}
42+
defer hash.Destroy()
43+
44+
hashBytes := hash.Bytes()
45+
if len(hashBytes) != 32 {
46+
t.Errorf("Expected hash length 32, got %d", len(hashBytes))
47+
}
48+
49+
// Expected genesis block hash (reversed byte order for display)
50+
expectedHash := "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
51+
actualHashHex := hex.EncodeToString(ReverseBytes(hashBytes))
52+
if actualHashHex != expectedHash {
53+
t.Logf("Actual hash: %s", actualHashHex)
54+
t.Logf("Expected hash: %s", expectedHash)
55+
}
56+
57+
// Test getting block data
58+
data, err := block.Data()
59+
if err != nil {
60+
t.Fatalf("Block.Data() error = %v", err)
61+
}
62+
63+
if len(data) != len(genesisBytes) {
64+
t.Errorf("Expected data length %d, got %d", len(genesisBytes), len(data))
65+
}
66+
67+
hexStr := hex.EncodeToString(data)
68+
if hexStr != genesisHex {
69+
t.Logf("Expected data hex: %s, got %s", genesisHex, hexStr)
70+
}
71+
}

kernel/errors.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,9 @@ var (
77
ErrContextOptionsCreation = errors.New("failed to create context options")
88
ErrContextCreation = errors.New("failed to create kernel context")
99
ErrInvalidChainType = errors.New("invalid chain type")
10+
ErrBlockCreation = errors.New("failed to create block from raw data")
11+
ErrInvalidBlockData = errors.New("invalid block data")
12+
ErrInvalidBlock = errors.New("invalid block")
13+
ErrHashCalculation = errors.New("failed to calculate hash")
14+
ErrBlockDataCopy = errors.New("failed to copy block data")
1015
)

kernel/utils.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package kernel
2+
3+
// ReverseBytes reverses bytes for display
4+
func ReverseBytes(data []byte) []byte {
5+
result := make([]byte, len(data))
6+
for i, b := range data {
7+
result[len(data)-1-i] = b
8+
}
9+
return result
10+
}

0 commit comments

Comments
 (0)