Skip to content
Open
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
18 changes: 14 additions & 4 deletions model/Block.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ type SubtreeStore interface {
}

func (b *Block) Valid(ctx context.Context, logger ulogger.Logger, subtreeStore SubtreeStore, txMetaStore utxo.Store, oldBlockIDsMap *txmap.SyncedMap[chainhash.Hash, []uint32],
currentChain []*BlockHeader, currentBlockHeaderIDs []uint32, settings *settings.Settings) (bool, error) {
currentChain []*BlockHeader, currentBlockHeaderIDs []uint32, settings *settings.Settings, metaRegenerator SubtreeMetaRegeneratorI) (bool, error) {
ctx, _, deferFn := tracing.Tracer("block").Start(ctx, "Valid",
tracing.WithHistogram(prometheusBlockValid),
tracing.WithLogMessage(logger, "[Block:Valid] called for %s", b.Header.String()),
Expand Down Expand Up @@ -511,6 +511,7 @@ func (b *Block) Valid(ctx context.Context, logger ulogger.Logger, subtreeStore S
currentChain: currentChain,
currentBlockHeaderIDs: currentBlockHeaderIDs,
oldBlockIDsMap: oldBlockIDsMap,
metaRegenerator: metaRegenerator,
}
err = b.validOrderAndBlessed(ctx, logger, deps, settings.Block.ValidOrderAndBlessedConcurrency)
if err != nil {
Expand Down Expand Up @@ -663,6 +664,7 @@ type validationDependencies struct {
currentChain []*BlockHeader
currentBlockHeaderIDs []uint32
oldBlockIDsMap *txmap.SyncedMap[chainhash.Hash, []uint32]
metaRegenerator SubtreeMetaRegeneratorI // optional: nil means no regeneration
}

func (b *Block) validOrderAndBlessed(ctx context.Context, logger ulogger.Logger, deps *validationDependencies, validOrderAndBlessedConcurrency int) error {
Expand Down Expand Up @@ -712,9 +714,17 @@ func (b *Block) validateSubtree(ctx context.Context, logger ulogger.Logger, deps
err error
)

subtreeMetaSlice, err = retry.Retry(ctx, logger, func() (*subtreepkg.Meta, error) {
return b.getSubtreeMetaSlice(ctx, deps.subtreeStore, *subtreeHash, subtree)
}, retry.WithMessage(fmt.Sprintf("[validOrderAndBlessed][%s][%s:%d] error getting subtree meta slice", b.String(), subtreeHash.String(), sIdx)))
subtreeMetaSlice, err = b.getSubtreeMetaSlice(ctx, deps.subtreeStore, *subtreeHash, subtree)

// Attempt regeneration if meta not found and regenerator is available
if err != nil && deps.metaRegenerator != nil {
logger.Warnf("[validateSubtree][%s][%s:%d] subtree meta not found, attempting regeneration", b.String(), subtreeHash.String(), sIdx)

subtreeMetaSlice, err = deps.metaRegenerator.RegenerateMeta(ctx, subtreeHash, subtree)
if err == nil {
logger.Warnf("[validateSubtree][%s][%s:%d] successfully regenerated subtree meta", b.String(), subtreeHash.String(), sIdx)
}
}

// a subtreeMetaSlice is required for further block validation, so if we cannot get it, we return an error
if err != nil {
Expand Down
42 changes: 21 additions & 21 deletions model/Block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ func TestBlock_Valid_ComprehensiveCoverage(t *testing.T) {
currentBlockHeaderIDs := []uint32{}

// This should hit many validation paths
valid, err := block.Valid(ctx, logger, subtreeStore, txMetaStore, oldBlockIDsMap, currentChain, currentBlockHeaderIDs, settings)
valid, err := block.Valid(ctx, logger, subtreeStore, txMetaStore, oldBlockIDsMap, currentChain, currentBlockHeaderIDs, settings, nil)
// May pass or fail, but we're testing coverage
_ = valid
_ = err
Expand Down Expand Up @@ -292,7 +292,7 @@ func TestBlock_Valid_ComprehensiveCoverage(t *testing.T) {
logger := ulogger.TestLogger{}

// This should fail validation (may hit difficulty or timestamp validation)
valid, err := block.Valid(ctx, logger, nil, createTestUTXOStore(t), txmap.NewSyncedMap[chainhash.Hash, []uint32](), []*BlockHeader{}, []uint32{}, tSettings)
valid, err := block.Valid(ctx, logger, nil, createTestUTXOStore(t), txmap.NewSyncedMap[chainhash.Hash, []uint32](), []*BlockHeader{}, []uint32{}, tSettings, nil)
assert.False(t, valid)
assert.Error(t, err) // Just verify it fails - the specific error depends on validation order
})
Expand All @@ -316,7 +316,7 @@ func TestBlock_Valid_ComprehensiveCoverage(t *testing.T) {
logger := ulogger.TestLogger{}

// This should hit the nil coinbase validation path
valid, err := block.Valid(ctx, logger, nil, createTestUTXOStore(t), txmap.NewSyncedMap[chainhash.Hash, []uint32](), []*BlockHeader{}, []uint32{}, tSettings)
valid, err := block.Valid(ctx, logger, nil, createTestUTXOStore(t), txmap.NewSyncedMap[chainhash.Hash, []uint32](), []*BlockHeader{}, []uint32{}, tSettings, nil)
assert.False(t, valid)
assert.Error(t, err)
assert.Contains(t, err.Error(), "no coinbase tx")
Expand Down Expand Up @@ -349,7 +349,7 @@ func TestBlock_Valid_ComprehensiveCoverage(t *testing.T) {
logger := ulogger.TestLogger{}

// This should hit the median timestamp validation path
valid, err := block.Valid(ctx, logger, nil, createTestUTXOStore(t), txmap.NewSyncedMap[chainhash.Hash, []uint32](), currentChain, []uint32{}, tSettings)
valid, err := block.Valid(ctx, logger, nil, createTestUTXOStore(t), txmap.NewSyncedMap[chainhash.Hash, []uint32](), currentChain, []uint32{}, tSettings, nil)
// May pass or fail, but we're testing the median timestamp code path
_ = valid
_ = err
Expand All @@ -374,7 +374,7 @@ func TestBlock_Valid_ComprehensiveCoverage(t *testing.T) {
logger := ulogger.TestLogger{}

// This should hit the coinbase height validation path
valid, err := block.Valid(ctx, logger, nil, createTestUTXOStore(t), txmap.NewSyncedMap[chainhash.Hash, []uint32](), []*BlockHeader{}, []uint32{}, tSettings)
valid, err := block.Valid(ctx, logger, nil, createTestUTXOStore(t), txmap.NewSyncedMap[chainhash.Hash, []uint32](), []*BlockHeader{}, []uint32{}, tSettings, nil)
// Will likely fail due to height mismatch, but we're testing the code path
_ = valid
_ = err
Expand Down Expand Up @@ -402,7 +402,7 @@ func TestBlock_Valid_ComprehensiveCoverage(t *testing.T) {
subtreeStore := &mockSubtreeStore{shouldError: true} // Empty store

// This should hit the subtree validation path
valid, err := block.Valid(ctx, logger, subtreeStore, createTestUTXOStore(t), txmap.NewSyncedMap[chainhash.Hash, []uint32](), []*BlockHeader{}, []uint32{}, tSettings)
valid, err := block.Valid(ctx, logger, subtreeStore, createTestUTXOStore(t), txmap.NewSyncedMap[chainhash.Hash, []uint32](), []*BlockHeader{}, []uint32{}, tSettings, nil)
// Will likely fail due to missing subtree, but we're testing the code path
_ = valid
_ = err
Expand All @@ -425,7 +425,7 @@ func TestBlock_Valid_ComprehensiveCoverage(t *testing.T) {
logger := ulogger.TestLogger{}

// This should skip median timestamp validation due to empty chain
valid, err := block.Valid(ctx, logger, nil, createTestUTXOStore(t), txmap.NewSyncedMap[chainhash.Hash, []uint32](), []*BlockHeader{}, []uint32{}, tSettings)
valid, err := block.Valid(ctx, logger, nil, createTestUTXOStore(t), txmap.NewSyncedMap[chainhash.Hash, []uint32](), []*BlockHeader{}, []uint32{}, tSettings, nil)
// Should hit the empty chain path
_ = valid
_ = err
Expand Down Expand Up @@ -1176,7 +1176,7 @@ func TestBlock_ValidWithOneTransaction(t *testing.T) {

currentChain[0].HashPrevBlock = &chainhash.Hash{}
oldBlockIDs := txmap.NewSyncedMap[chainhash.Hash, []uint32]()
v, err := b.Valid(context.Background(), ulogger.TestLogger{}, subtreeStore, utxoStore, oldBlockIDs, currentChain, currentChainIDs, settings)
v, err := b.Valid(context.Background(), ulogger.TestLogger{}, subtreeStore, utxoStore, oldBlockIDs, currentChain, currentChainIDs, settings, nil)
require.NoError(t, err)
require.True(t, v)

Expand Down Expand Up @@ -2063,7 +2063,7 @@ func TestBlock_Valid_MoreCoverage(t *testing.T) {

// Call with txMetaStore to trigger validOrderAndBlessed path
valid, err := block.Valid(ctx, logger, mockBlobStore, txMetaStore, oldBlockIDs,
[]*BlockHeader{}, []uint32{}, tSettings)
[]*BlockHeader{}, []uint32{}, tSettings, nil)

// This might error due to missing subtrees, but we're testing the path
_ = valid
Expand Down Expand Up @@ -2296,23 +2296,23 @@ func TestTargetedCoverageIncrease(t *testing.T) {

// Test with nil subtreeStore to skip the subtree check
valid, err := block.Valid(ctx, logger, nil, nil, oldBlockIDs,
[]*BlockHeader{}, []uint32{}, tSettings)
[]*BlockHeader{}, []uint32{}, tSettings, nil)

// Should succeed because we're skipping most validation
require.NoError(t, err)
assert.True(t, valid)

// Test with subtreeStore but no txMetaStore to test different paths
valid, err = block.Valid(ctx, logger, mockSubtreeStore, nil, oldBlockIDs,
[]*BlockHeader{}, []uint32{}, tSettings)
[]*BlockHeader{}, []uint32{}, tSettings, nil)

// This will error due to missing subtrees but tests the path
_ = valid
_ = err

// Test with txMetaStore to trigger validOrderAndBlessed
valid, err = block.Valid(ctx, logger, nil, txMetaStore, oldBlockIDs,
[]*BlockHeader{}, []uint32{}, tSettings)
[]*BlockHeader{}, []uint32{}, tSettings, nil)

_ = valid
_ = err
Expand Down Expand Up @@ -2419,14 +2419,14 @@ func TestAdditionalCoverageFunctions(t *testing.T) {
// Test with only subtreeStore
mockSubtreeStore := &mockSubtreeStore{shouldError: true}
_, err = block.Valid(ctx, logger, mockSubtreeStore, nil, oldBlockIDs,
[]*BlockHeader{}, []uint32{}, tSettings)
[]*BlockHeader{}, []uint32{}, tSettings, nil)
// Will error but exercises the subtree validation path
_ = err

// Test checkBlockRewardAndFees path with height > 0
block.Height = 100
_, err = block.Valid(ctx, logger, nil, nil, oldBlockIDs,
[]*BlockHeader{}, []uint32{}, tSettings)
[]*BlockHeader{}, []uint32{}, tSettings, nil)
// Will error but exercises checkBlockRewardAndFees path
_ = err
})
Expand Down Expand Up @@ -2582,7 +2582,7 @@ func TestMaximumCoverageBoost(t *testing.T) {

block1.SubtreeSlices = []*subtreepkg.Subtree{subtree}
_, err = block1.Valid(ctx, logger, nil, nil, oldBlockIDs,
[]*BlockHeader{}, []uint32{}, tSettings)
[]*BlockHeader{}, []uint32{}, tSettings, nil)
_ = err // Exercises checkBlockRewardAndFees path safely

// Test path 2: GetAndValidateSubtrees path
Expand All @@ -2594,15 +2594,15 @@ func TestMaximumCoverageBoost(t *testing.T) {
mockSubtreeStore := &mockSubtreeStore{shouldError: true}

_, err = block2.Valid(ctx, logger, mockSubtreeStore, nil, oldBlockIDs,
[]*BlockHeader{}, []uint32{}, tSettings)
[]*BlockHeader{}, []uint32{}, tSettings, nil)
_ = err // Exercises GetAndValidateSubtrees path

// Test path 3: validOrderAndBlessed path with txMetaStore
block3, err := NewBlock(blockHeader, coinbase, []*chainhash.Hash{}, 1, 123, 0, 0)
require.NoError(t, err)
txMetaStore := createTestUTXOStore(t)
_, err = block3.Valid(ctx, logger, nil, txMetaStore, oldBlockIDs,
[]*BlockHeader{}, []uint32{}, tSettings)
[]*BlockHeader{}, []uint32{}, tSettings, nil)
_ = err // Exercises validOrderAndBlessed path

// Test path 4: CheckMerkleRoot path
Expand All @@ -2617,7 +2617,7 @@ func TestMaximumCoverageBoost(t *testing.T) {

block4.SubtreeSlices = []*subtreepkg.Subtree{subtree2}
_, err = block4.Valid(ctx, logger, nil, nil, oldBlockIDs,
[]*BlockHeader{}, []uint32{}, tSettings)
[]*BlockHeader{}, []uint32{}, tSettings, nil)
_ = err // Exercises CheckMerkleRoot path
})

Expand Down Expand Up @@ -4134,7 +4134,7 @@ func TestBlock_Valid_CoinbasePlaceholderCheck(t *testing.T) {

// This should fail the coinbase placeholder check
oldBlockIDsMap := txmap.NewSyncedMap[chainhash.Hash, []uint32]()
valid, err := block.Valid(ctx, logger, mockBlobStore, txMetaStore, oldBlockIDsMap, []*BlockHeader{}, []uint32{}, tSettings)
valid, err := block.Valid(ctx, logger, mockBlobStore, txMetaStore, oldBlockIDsMap, []*BlockHeader{}, []uint32{}, tSettings, nil)
require.Error(t, err)
require.False(t, valid)
assert.Contains(t, err.Error(), "first transaction in first subtree is not a coinbase placeholder")
Expand Down Expand Up @@ -4184,7 +4184,7 @@ func TestBlock_Valid_CoinbasePlaceholderCheck(t *testing.T) {

// This should fail validation - coinbase placeholder must be in first subtree, first position
oldBlockIDsMap := txmap.NewSyncedMap[chainhash.Hash, []uint32]()
valid, err := block.Valid(ctx, logger, mockBlobStore, txMetaStore, oldBlockIDsMap, []*BlockHeader{}, []uint32{}, tSettings)
valid, err := block.Valid(ctx, logger, mockBlobStore, txMetaStore, oldBlockIDsMap, []*BlockHeader{}, []uint32{}, tSettings, nil)
require.Error(t, err)
require.False(t, valid)
assert.Contains(t, err.Error(), "first transaction in first subtree is not a coinbase placeholder")
Expand Down Expand Up @@ -4216,7 +4216,7 @@ func TestBlock_Valid_CoinbasePlaceholderCheck(t *testing.T) {
// With empty subtree slices, the validation should pass this check
// (it will fail on other validations)
oldBlockIDsMap := txmap.NewSyncedMap[chainhash.Hash, []uint32]()
valid, err := block.Valid(ctx, logger, mockBlobStore, txMetaStore, oldBlockIDsMap, []*BlockHeader{}, []uint32{}, tSettings)
valid, err := block.Valid(ctx, logger, mockBlobStore, txMetaStore, oldBlockIDsMap, []*BlockHeader{}, []uint32{}, tSettings, nil)
_ = valid
_ = err
// The coinbase placeholder check should be skipped for empty subtrees
Expand Down
Loading
Loading