mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-10-14 00:59:33 +00:00

* [NOD-420] Delay blocks with valid timestamp (non-delayed) that point to a delayed block. * [NOD-420] Mark block as requested when setting as delayed. * [NOD-420] Merge master; Use dag.timeSource.AdjustedTime() instead of time.Now; * [NOD-420] Return nil when not expecting an error * [NOD-420] Initialise delyaed blocks mapping * [NOD-420] Trigger delayed blocks processing every time we process a block. * [NOD-420] Hold the read lock in processDelayedBlocks * [NOD-420] Add delayed blocks heap sorted by their process time so we could process them in order. * [NOD-420] Update debug log * [NOD-420] Fix process blocks loop * [NOD-420] Add comment * [NOD-420] Log error message * [NOD-420] Implement peek method for delayed block heap. extract delayed block processing to another function. * [NOD-420] Trigger process delayed blocks only in process block * [NOD-420] Move delayed block addition to process block * [NOD-420] Use process block to make sure we fully process the delayed block and deal with orphans. * [NOD-420] Unexport functions when not needed; Return isDelayed boolean from ProcessBlock instead of the delay duration * [NOd-420] Remove redundant delayedBlocksLock * [NOD-420] Resolve merge conflict; Return delay 0 instead of boolean * [NOD-420] Do not treat delayed block as orphan * [NOD-420] Make sure block is not processed if we have already sa delayed. * [NOD-420] Process delayed block if parent is delayed to make sure it would not be treated as orphan. * [NOD-420] Rename variable * [NOD-420] Rename function. Move maxDelayOfParents to process.go * [NOD-420] Fix typo * [NOD-420] Handle errors from processDelayedBlocks properly * [NOD-420] Return default values if err != nil from dag.addDelayedBlock * [NOD-420] Return default values if err != nil from dag.addDelayedBlock in another place Co-authored-by: Svarog <feanorr@gmail.com>
144 lines
5.1 KiB
Go
144 lines
5.1 KiB
Go
package blockdag
|
|
|
|
import (
|
|
"github.com/pkg/errors"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"bou.ke/monkey"
|
|
"github.com/kaspanet/kaspad/dagconfig"
|
|
"github.com/kaspanet/kaspad/database"
|
|
"github.com/kaspanet/kaspad/util"
|
|
)
|
|
|
|
func TestMaybeAcceptBlockErrors(t *testing.T) {
|
|
// Create a new database and DAG instance to run tests against.
|
|
dag, teardownFunc, err := DAGSetup("TestMaybeAcceptBlockErrors", Config{
|
|
DAGParams: &dagconfig.SimNetParams,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("TestMaybeAcceptBlockErrors: Failed to setup DAG instance: %v", err)
|
|
}
|
|
defer teardownFunc()
|
|
|
|
dag.TestSetCoinbaseMaturity(0)
|
|
|
|
// Test rejecting the block if its parents are missing
|
|
orphanBlockFile := "blk_3B.dat"
|
|
loadedBlocks, err := LoadBlocks(filepath.Join("testdata/", orphanBlockFile))
|
|
if err != nil {
|
|
t.Fatalf("TestMaybeAcceptBlockErrors: "+
|
|
"Error loading file '%s': %s\n", orphanBlockFile, err)
|
|
}
|
|
block := loadedBlocks[0]
|
|
|
|
err = dag.maybeAcceptBlock(block, BFNone)
|
|
if err == nil {
|
|
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are missing: "+
|
|
"Expected: %s, got: <nil>", ErrParentBlockUnknown)
|
|
}
|
|
ruleErr, ok := err.(RuleError)
|
|
if !ok {
|
|
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are missing: "+
|
|
"Expected RuleError but got %s", err)
|
|
} else if ruleErr.ErrorCode != ErrParentBlockUnknown {
|
|
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are missing: "+
|
|
"Unexpected error code. Want: %s, got: %s", ErrParentBlockUnknown, ruleErr.ErrorCode)
|
|
}
|
|
|
|
// Test rejecting the block if its parents are invalid
|
|
blocksFile := "blk_0_to_4.dat"
|
|
blocks, err := LoadBlocks(filepath.Join("testdata/", blocksFile))
|
|
if err != nil {
|
|
t.Fatalf("TestMaybeAcceptBlockErrors: "+
|
|
"Error loading file '%s': %s\n", blocksFile, err)
|
|
}
|
|
|
|
// Add a valid block and mark it as invalid
|
|
block1 := blocks[1]
|
|
isOrphan, isDelayed, err := dag.ProcessBlock(block1, BFNone)
|
|
if err != nil {
|
|
t.Fatalf("TestMaybeAcceptBlockErrors: Valid block unexpectedly returned an error: %s", err)
|
|
}
|
|
if isDelayed {
|
|
t.Fatalf("TestMaybeAcceptBlockErrors: block 1 is too far in the future")
|
|
}
|
|
if isOrphan {
|
|
t.Fatalf("TestMaybeAcceptBlockErrors: incorrectly returned block 1 is an orphan")
|
|
}
|
|
blockNode1 := dag.index.LookupNode(block1.Hash())
|
|
dag.index.SetStatusFlags(blockNode1, statusValidateFailed)
|
|
|
|
block2 := blocks[2]
|
|
err = dag.maybeAcceptBlock(block2, BFNone)
|
|
if err == nil {
|
|
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are invalid: "+
|
|
"Expected: %s, got: <nil>", ErrInvalidAncestorBlock)
|
|
}
|
|
ruleErr, ok = err.(RuleError)
|
|
if !ok {
|
|
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are invalid: "+
|
|
"Expected RuleError but got %s", err)
|
|
} else if ruleErr.ErrorCode != ErrInvalidAncestorBlock {
|
|
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are invalid: "+
|
|
"Unexpected error. Want: %s, got: %s", ErrInvalidAncestorBlock, ruleErr.ErrorCode)
|
|
}
|
|
|
|
// Set block1's status back to valid for next tests
|
|
dag.index.UnsetStatusFlags(blockNode1, statusValidateFailed)
|
|
|
|
// Test rejecting the block due to bad context
|
|
originalBits := block2.MsgBlock().Header.Bits
|
|
block2.MsgBlock().Header.Bits = 0
|
|
err = dag.maybeAcceptBlock(block2, BFNone)
|
|
if err == nil {
|
|
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block due to bad context: "+
|
|
"Expected: %s, got: <nil>", ErrUnexpectedDifficulty)
|
|
}
|
|
ruleErr, ok = err.(RuleError)
|
|
if !ok {
|
|
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block due to bad context: "+
|
|
"Expected RuleError but got %s", err)
|
|
} else if ruleErr.ErrorCode != ErrUnexpectedDifficulty {
|
|
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block due to bad context: "+
|
|
"Unexpected error. Want: %s, got: %s", ErrUnexpectedDifficulty, ruleErr.ErrorCode)
|
|
}
|
|
|
|
// Set block2's bits back to valid for next tests
|
|
block2.MsgBlock().Header.Bits = originalBits
|
|
|
|
// Test rejecting the node due to database error
|
|
databaseErrorMessage := "database error"
|
|
guard := monkey.Patch(dbStoreBlock, func(dbTx database.Tx, block *util.Block) error {
|
|
return errors.New(databaseErrorMessage)
|
|
})
|
|
defer guard.Unpatch()
|
|
err = dag.maybeAcceptBlock(block2, BFNone)
|
|
if err == nil {
|
|
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the node due to database error: "+
|
|
"Expected: %s, got: <nil>", databaseErrorMessage)
|
|
}
|
|
if !strings.Contains(err.Error(), databaseErrorMessage) {
|
|
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the node due to database error: "+
|
|
"Unexpected error. Want: %s, got: %s", databaseErrorMessage, err)
|
|
}
|
|
guard.Unpatch()
|
|
|
|
// Test rejecting the node due to index error
|
|
indexErrorMessage := "index error"
|
|
guard = monkey.Patch((*blockIndex).flushToDB, func(_ *blockIndex) error {
|
|
return errors.New(indexErrorMessage)
|
|
})
|
|
defer guard.Unpatch()
|
|
err = dag.maybeAcceptBlock(block2, BFNone)
|
|
if err == nil {
|
|
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the node due to index error: "+
|
|
"Expected %s, got: <nil>", indexErrorMessage)
|
|
}
|
|
if !strings.Contains(err.Error(), indexErrorMessage) {
|
|
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the node due to index error: "+
|
|
"Unexpected error. Want: %s, got: %s", indexErrorMessage, err)
|
|
}
|
|
}
|