Skip to content

Commit 8c628cf

Browse files
committed
Add BlockUndo
1 parent ae56549 commit 8c628cf

File tree

5 files changed

+131
-11
lines changed

5 files changed

+131
-11
lines changed

TODO.md

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ This document lists the remaining C API functions and data structures from [`bit
44

55
## Missing Data Structures
66

7-
### Core Transaction Types
87
- **`kernel_BlockPointer`** - Non-owned block pointers (from callbacks)
9-
- **`kernel_BlockUndo`** - Block undo data operations
108

119
## Missing Functions by Category
1210

@@ -15,15 +13,7 @@ This document lists the remaining C API functions and data structures from [`bit
1513

1614
### Block Operations (Additional)
1715
- [ ] `kernel_block_pointer_get_hash()` - Get hash from block pointer
18-
- [ ] `kernel_copy_block_pointer_data()` - Copy data from block pointer
19-
20-
### Block Undo Operations
21-
- [ ] `kernel_read_block_undo_from_disk()` - Read undo data from disk
22-
- [ ] `kernel_block_undo_size()` - Get number of transactions in undo data
23-
- [ ] `kernel_get_transaction_undo_size()` - Get output count per transaction
24-
- [ ] `kernel_get_undo_output_height_by_index()` - Get output block height
25-
- [ ] `kernel_get_undo_output_by_index()` - Get specific undo output
26-
- [ ] `kernel_block_undo_destroy()` - Cleanup undo data
16+
- [ ] `kernel_copy_block_pointer_data()` - Copy data from block pointer
2717

2818
### Callback Support
2919
- [ ] **Notification callbacks** - Full integration of kernel notification system

kernel/block_undo.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+
// BlockUndo wraps the C kernel_BlockUndo
12+
type BlockUndo struct {
13+
ptr *C.kernel_BlockUndo
14+
}
15+
16+
// Size returns the number of transactions whose undo data is contained in block undo
17+
func (bu *BlockUndo) Size() uint64 {
18+
if bu.ptr == nil {
19+
return 0
20+
}
21+
return uint64(C.kernel_block_undo_size(bu.ptr))
22+
}
23+
24+
// GetTransactionUndoSize returns the number of previous transaction outputs
25+
// contained in the transaction undo data at the specified index
26+
func (bu *BlockUndo) GetTransactionUndoSize(transactionUndoIndex uint64) uint64 {
27+
if bu.ptr == nil {
28+
return 0
29+
}
30+
return uint64(C.kernel_get_transaction_undo_size(bu.ptr, C.uint64_t(transactionUndoIndex)))
31+
}
32+
33+
// GetUndoOutputHeightByIndex returns the block height of the block that contains
34+
// the output at output_index within the transaction undo data at the provided index
35+
func (bu *BlockUndo) GetUndoOutputHeightByIndex(transactionUndoIndex, outputIndex uint64) uint32 {
36+
if bu.ptr == nil {
37+
return 0
38+
}
39+
return uint32(C.kernel_get_undo_output_height_by_index(bu.ptr, C.uint64_t(transactionUndoIndex), C.uint64_t(outputIndex)))
40+
}
41+
42+
// GetUndoOutputByIndex returns a transaction output contained in the transaction
43+
// undo data at the specified indices
44+
func (bu *BlockUndo) GetUndoOutputByIndex(transactionUndoIndex, outputIndex uint64) (*TransactionOutput, error) {
45+
if bu.ptr == nil {
46+
return nil, ErrInvalidBlockUndo
47+
}
48+
49+
ptr := C.kernel_get_undo_output_by_index(bu.ptr, C.uint64_t(transactionUndoIndex), C.uint64_t(outputIndex))
50+
if ptr == nil {
51+
return nil, ErrUndoOutputRetrieval
52+
}
53+
54+
output := &TransactionOutput{ptr: ptr}
55+
runtime.SetFinalizer(output, (*TransactionOutput).destroy)
56+
return output, nil
57+
}
58+
59+
func (bu *BlockUndo) destroy() {
60+
if bu.ptr != nil {
61+
C.kernel_block_undo_destroy(bu.ptr)
62+
bu.ptr = nil
63+
}
64+
}
65+
66+
func (bu *BlockUndo) Destroy() {
67+
runtime.SetFinalizer(bu, nil)
68+
bu.destroy()
69+
}

kernel/chainstate_manager.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,25 @@ func (cm *ChainstateManager) ReadBlockFromDisk(blockIndex *BlockIndex) (*Block,
6060
return block, nil
6161
}
6262

63+
// ReadBlockUndoFromDisk reads block undo data from disk for a given block index
64+
func (cm *ChainstateManager) ReadBlockUndoFromDisk(blockIndex *BlockIndex) (*BlockUndo, error) {
65+
if cm.ptr == nil || cm.context == nil || cm.context.ptr == nil {
66+
return nil, ErrChainstateManagerCreation
67+
}
68+
if blockIndex == nil || blockIndex.ptr == nil {
69+
return nil, ErrInvalidBlockIndex
70+
}
71+
72+
ptr := C.kernel_read_block_undo_from_disk(cm.context.ptr, cm.ptr, blockIndex.ptr)
73+
if ptr == nil {
74+
return nil, ErrBlockUndoRead
75+
}
76+
77+
blockUndo := &BlockUndo{ptr: ptr}
78+
runtime.SetFinalizer(blockUndo, (*BlockUndo).destroy)
79+
return blockUndo, nil
80+
}
81+
6382
// ProcessBlock processes and validates a block
6483
func (cm *ChainstateManager) ProcessBlock(block *Block) (bool, bool, error) {
6584
if cm.ptr == nil || cm.context == nil || cm.context.ptr == nil {

kernel/chainstate_manager_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ func TestChainstateManager(t *testing.T) {
1313

1414
t.Run("genesis validation", suite.TestGenesis)
1515
t.Run("tip validation", suite.TestTip)
16+
t.Run("block undo", suite.TestBlockUndo)
1617
}
1718

1819
func (s *ChainstateManagerTestSuite) TestGenesis(t *testing.T) {
@@ -67,6 +68,44 @@ func (s *ChainstateManagerTestSuite) TestTip(t *testing.T) {
6768
}
6869
}
6970

71+
func (s *ChainstateManagerTestSuite) TestBlockUndo(t *testing.T) {
72+
blockIndex, err := s.Manager.GetBlockIndexFromHeight(202)
73+
if err != nil {
74+
t.Fatalf("GetBlockIndexFromHeight(202) error = %v", err)
75+
}
76+
defer blockIndex.Destroy()
77+
78+
blockUndo, err := s.Manager.ReadBlockUndoFromDisk(blockIndex)
79+
if err != nil {
80+
t.Fatalf("ReadBlockUndoFromDisk() error = %v", err)
81+
}
82+
defer blockUndo.Destroy()
83+
84+
// Test transaction count
85+
txCount := blockUndo.Size()
86+
if txCount != 20 {
87+
t.Errorf("Expected 20 transactions, got %d", txCount)
88+
}
89+
90+
// Verify each transaction is a valid TransactionUndo
91+
for i := uint64(0); i < txCount; i++ {
92+
undoSize := blockUndo.GetTransactionUndoSize(i)
93+
if undoSize != 1 {
94+
t.Errorf("Expected transaction undo size 1, got %d", undoSize)
95+
}
96+
97+
_, err := blockUndo.GetUndoOutputByIndex(i, 0)
98+
if err != nil {
99+
t.Fatalf("GetUndoOutputByIndex() error = %v", err)
100+
}
101+
102+
height := blockUndo.GetUndoOutputHeightByIndex(i, 0)
103+
if height <= 0 {
104+
t.Fatalf("GetUndoOutputHeightByIndex() height %d, want > 0", height)
105+
}
106+
}
107+
}
108+
70109
type ChainstateManagerTestSuite struct {
71110
Manager *ChainstateManager
72111
ImportedBlocksCount int32

kernel/errors.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,7 @@ var (
2828
ErrTransactionOutputCreation = errors.New("failed to create transaction output")
2929
ErrInvalidTransactionOutput = errors.New("invalid transaction output")
3030
ErrScriptPubkeyCopyFromOutput = errors.New("failed to copy script pubkey from output")
31+
ErrBlockUndoRead = errors.New("failed to read block undo from disk")
32+
ErrInvalidBlockUndo = errors.New("invalid block undo")
33+
ErrUndoOutputRetrieval = errors.New("failed to retrieve undo output")
3134
)

0 commit comments

Comments
 (0)