[NOD-648] Add TestProcessDelayedBlocks (#612)

* [NOD-648] Add TestProcessDelayedBlocks

* [NOD-648] Add one second to secondsUntilDelayedBlockIsValid to make sure the delayedBlock timestamp will be valid, and add comments

* [NOD-648] Remove redundant import

* [NOD-648] Use fakeTimeSource instead of time.Sleep

* [NOD-648] Rename dag.HaveBlock->dag.IsKnownBlock,  dag.BlockExists->dag.IsInDAG

* [NOD-648] Add comment

* [NOD-641] Rename HaveBlock->IsKnownBlock, BlockExists->IsInDAG
This commit is contained in:
Ori Newman 2020-02-03 11:30:03 +02:00 committed by GitHub
parent aa74b51e6f
commit 41c8178ad3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 195 additions and 28 deletions

View File

@ -153,23 +153,23 @@ type BlockDAG struct {
reachabilityStore *reachabilityStore
}
// HaveBlock returns whether or not the DAG instance has the block represented
// IsKnownBlock returns whether or not the DAG instance has the block represented
// by the passed hash. This includes checking the various places a block can
// be in, like part of the DAG or the orphan pool.
//
// This function is safe for concurrent access.
func (dag *BlockDAG) HaveBlock(hash *daghash.Hash) bool {
return dag.BlockExists(hash) || dag.IsKnownOrphan(hash) || dag.isKnownDelayedBlock(hash)
func (dag *BlockDAG) IsKnownBlock(hash *daghash.Hash) bool {
return dag.IsInDAG(hash) || dag.IsKnownOrphan(hash) || dag.isKnownDelayedBlock(hash)
}
// HaveBlocks returns whether or not the DAG instances has all blocks represented
// AreKnownBlocks returns whether or not the DAG instances has all blocks represented
// by the passed hashes. This includes checking the various places a block can
// be in, like part of the DAG or the orphan pool.
//
// This function is safe for concurrent access.
func (dag *BlockDAG) HaveBlocks(hashes []*daghash.Hash) bool {
func (dag *BlockDAG) AreKnownBlocks(hashes []*daghash.Hash) bool {
for _, hash := range hashes {
haveBlock := dag.HaveBlock(hash)
haveBlock := dag.IsKnownBlock(hash)
if !haveBlock {
return false
}
@ -234,7 +234,7 @@ func (dag *BlockDAG) GetOrphanMissingAncestorHashes(orphanHash *daghash.Hash) ([
queue = append(queue, parentHash)
}
} else {
if !dag.BlockExists(current) && current != orphanHash {
if !dag.IsInDAG(current) && current != orphanHash {
missingAncestorsHashes = append(missingAncestorsHashes, current)
}
}
@ -1501,7 +1501,7 @@ func (dag *BlockDAG) SelectedParentChain(blockHash *daghash.Hash) ([]*daghash.Ha
if blockHash == nil {
blockHash = dag.genesis.hash
}
if !dag.BlockExists(blockHash) {
if !dag.IsInDAG(blockHash) {
return nil, nil, errors.Errorf("blockHash %s does not exist in the DAG", blockHash)
}

View File

@ -72,8 +72,8 @@ func TestBlockCount(t *testing.T) {
}
}
// TestHaveBlock tests the HaveBlock API to ensure proper functionality.
func TestHaveBlock(t *testing.T) {
// TestIsKnownBlock tests the IsKnownBlock API to ensure proper functionality.
func TestIsKnownBlock(t *testing.T) {
// Load up blocks such that there is a fork in the DAG.
// (genesis block) -> 1 -> 2 -> 3 -> 4
// \-> 3b
@ -217,9 +217,9 @@ func TestHaveBlock(t *testing.T) {
t.Fatalf("NewHashFromStr: %v", err)
}
result := dag.HaveBlock(hash)
result := dag.IsKnownBlock(hash)
if result != test.want {
t.Fatalf("HaveBlock #%d got %v want %v", i, result,
t.Fatalf("IsKnownBlock #%d got %v want %v", i, result,
test.want)
}
}

View File

@ -873,7 +873,7 @@ func (dag *BlockDAG) BlockHashesFrom(lowHash *daghash.Hash, limit int) ([]*dagha
// genesis hash in the result
blockHashes = append(blockHashes, dag.genesis.hash)
}
if !dag.BlockExists(lowHash) {
if !dag.IsInDAG(lowHash) {
return nil, errors.Errorf("block %s not found", lowHash)
}
blueScore, err := dag.BlueScoreByBlockHash(lowHash)

View File

@ -48,11 +48,11 @@ const (
BFNone BehaviorFlags = 0
)
// BlockExists determines whether a block with the given hash exists in
// IsInDAG determines whether a block with the given hash exists in
// the DAG.
//
// This function is safe for concurrent access.
func (dag *BlockDAG) BlockExists(hash *daghash.Hash) bool {
func (dag *BlockDAG) IsInDAG(hash *daghash.Hash) bool {
return dag.index.HaveBlock(hash)
}
@ -149,7 +149,7 @@ func (dag *BlockDAG) processBlockNoLock(block *util.Block, flags BehaviorFlags)
log.Tracef("Processing block %s", blockHash)
// The block must not already exist in the DAG.
if dag.BlockExists(blockHash) && !wasBlockStored {
if dag.IsInDAG(blockHash) && !wasBlockStored {
str := fmt.Sprintf("already have block %s", blockHash)
return false, false, ruleError(ErrDuplicateBlock, str)
}
@ -183,7 +183,7 @@ func (dag *BlockDAG) processBlockNoLock(block *util.Block, flags BehaviorFlags)
var missingParents []*daghash.Hash
for _, parentHash := range block.MsgBlock().Header.ParentHashes {
if !dag.BlockExists(parentHash) {
if !dag.IsInDAG(parentHash) {
missingParents = append(missingParents, parentHash)
}
}

View File

@ -1,8 +1,10 @@
package blockdag
import (
"github.com/kaspanet/kaspad/util"
"path/filepath"
"testing"
"time"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/util/daghash"
@ -69,3 +71,168 @@ func TestProcessOrphans(t *testing.T) {
t.Fatalf("TestProcessOrphans: child block erroneously not marked as invalid")
}
}
type fakeTimeSource struct {
time time.Time
}
func (fts *fakeTimeSource) AdjustedTime() time.Time {
return fts.time
}
func (fts *fakeTimeSource) AddTimeSample(_ string, _ time.Time) {
}
func (fts *fakeTimeSource) Offset() time.Duration {
return 0
}
func TestProcessDelayedBlocks(t *testing.T) {
// We use dag1 so we can build the test blocks with the proper
// block header (UTXO commitment, acceptedIDMerkleroot, etc), and
// then we use dag2 for the actual test.
dag1, teardownFunc, err := DAGSetup("TestProcessDelayedBlocks1", Config{
DAGParams: &dagconfig.SimnetParams,
})
if err != nil {
t.Fatalf("Failed to setup DAG instance: %v", err)
}
defer teardownFunc()
initialTime := dag1.dagParams.GenesisBlock.Header.Timestamp
// Here we use a fake time source that returns a timestamp
// one hour into the future to make delayedBlock artificially
// valid.
dag1.timeSource = &fakeTimeSource{initialTime.Add(time.Hour)}
delayedBlock, err := PrepareBlockForTest(dag1, []*daghash.Hash{dag1.dagParams.GenesisBlock.BlockHash()}, nil)
if err != nil {
t.Fatalf("error in PrepareBlockForTest: %s", err)
}
blockDelay := time.Duration(dag1.dagParams.TimestampDeviationTolerance+5) * time.Second
delayedBlock.Header.Timestamp = initialTime.Add(blockDelay)
isOrphan, isDelayed, err := dag1.ProcessBlock(util.NewBlock(delayedBlock), BFNoPoWCheck)
if err != nil {
t.Fatalf("ProcessBlock returned unexpected error: %s\n", err)
}
if isOrphan {
t.Fatalf("ProcessBlock incorrectly returned delayedBlock " +
"is an orphan\n")
}
if isDelayed {
t.Fatalf("ProcessBlock incorrectly returned delayedBlock " +
"is delayed\n")
}
delayedBlockChild, err := PrepareBlockForTest(dag1, []*daghash.Hash{delayedBlock.BlockHash()}, nil)
if err != nil {
t.Fatalf("error in PrepareBlockForTest: %s", err)
}
// Here the actual test begins. We add a delayed block and
// its child and check that they are not added to the DAG,
// and check that they're added only if we add a new block
// after the delayed block timestamp is valid.
dag2, teardownFunc2, err := DAGSetup("TestProcessDelayedBlocks2", Config{
DAGParams: &dagconfig.SimnetParams,
})
if err != nil {
t.Fatalf("Failed to setup DAG instance: %v", err)
}
defer teardownFunc2()
dag2.timeSource = &fakeTimeSource{initialTime}
isOrphan, isDelayed, err = dag2.ProcessBlock(util.NewBlock(delayedBlock), BFNoPoWCheck)
if err != nil {
t.Fatalf("ProcessBlock returned unexpected error: %s\n", err)
}
if isOrphan {
t.Fatalf("ProcessBlock incorrectly returned delayedBlock " +
"is an orphan\n")
}
if !isDelayed {
t.Fatalf("ProcessBlock incorrectly returned delayedBlock " +
"is not delayed\n")
}
if dag2.IsInDAG(delayedBlock.BlockHash()) {
t.Errorf("dag.IsInDAG should return false for a delayed block")
}
if !dag2.IsKnownBlock(delayedBlock.BlockHash()) {
t.Errorf("dag.IsKnownBlock should return true for a a delayed block")
}
isOrphan, isDelayed, err = dag2.ProcessBlock(util.NewBlock(delayedBlockChild), BFNoPoWCheck)
if err != nil {
t.Fatalf("ProcessBlock returned unexpected error: %s\n", err)
}
if isOrphan {
t.Fatalf("ProcessBlock incorrectly returned delayedBlockChild " +
"is an orphan\n")
}
if !isDelayed {
t.Fatalf("ProcessBlock incorrectly returned delayedBlockChild " +
"is not delayed\n")
}
if dag2.IsInDAG(delayedBlockChild.BlockHash()) {
t.Errorf("dag.IsInDAG should return false for a child of a delayed block")
}
if !dag2.IsKnownBlock(delayedBlockChild.BlockHash()) {
t.Errorf("dag.IsKnownBlock should return true for a child of a delayed block")
}
blockBeforeDelay, err := PrepareBlockForTest(dag2, []*daghash.Hash{dag2.dagParams.GenesisBlock.BlockHash()}, nil)
if err != nil {
t.Fatalf("error in PrepareBlockForTest: %s", err)
}
isOrphan, isDelayed, err = dag2.ProcessBlock(util.NewBlock(blockBeforeDelay), BFNoPoWCheck)
if err != nil {
t.Fatalf("ProcessBlock returned unexpected error: %s\n", err)
}
if isOrphan {
t.Fatalf("ProcessBlock incorrectly returned blockBeforeDelay " +
"is an orphan\n")
}
if isDelayed {
t.Fatalf("ProcessBlock incorrectly returned blockBeforeDelay " +
"is delayed\n")
}
if dag2.IsInDAG(delayedBlock.BlockHash()) {
t.Errorf("delayedBlock shouldn't be added to the DAG because its time hasn't reached yet")
}
if dag2.IsInDAG(delayedBlockChild.BlockHash()) {
t.Errorf("delayedBlockChild shouldn't be added to the DAG because its parent is not in the DAG")
}
// We advance the clock to the point where delayedBlock timestamp is valid.
secondsUntilDelayedBlockIsValid := delayedBlock.Header.Timestamp.Unix() - int64(dag2.TimestampDeviationTolerance) - dag2.AdjustedTime().Unix() + 1
dag2.timeSource = &fakeTimeSource{initialTime.Add(time.Duration(secondsUntilDelayedBlockIsValid) * time.Second)}
blockAfterDelay, err := PrepareBlockForTest(dag2, []*daghash.Hash{dag2.dagParams.GenesisBlock.BlockHash()}, nil)
if err != nil {
t.Fatalf("error in PrepareBlockForTest: %s", err)
}
isOrphan, isDelayed, err = dag2.ProcessBlock(util.NewBlock(blockAfterDelay), BFNoPoWCheck)
if err != nil {
t.Fatalf("ProcessBlock returned unexpected error: %s\n", err)
}
if isOrphan {
t.Fatalf("ProcessBlock incorrectly returned blockBeforeDelay " +
"is an orphan\n")
}
if isDelayed {
t.Fatalf("ProcessBlock incorrectly returned blockBeforeDelay " +
"is not delayed\n")
}
if !dag2.IsInDAG(delayedBlock.BlockHash()) {
t.Fatalf("delayedBlock should be added to the DAG because its time has been reached")
}
if !dag2.IsInDAG(delayedBlockChild.BlockHash()) {
t.Errorf("delayedBlockChild shouldn't be added to the DAG because its parent has been added to the DAG")
}
}

View File

@ -101,14 +101,14 @@ func (bi *blockImporter) processBlock(serializedBlock []byte) (bool, error) {
// Skip blocks that already exist.
blockHash := block.Hash()
if bi.dag.HaveBlock(blockHash) {
if bi.dag.IsKnownBlock(blockHash) {
return false, nil
}
// Don't bother trying to process orphans.
parentHashes := block.MsgBlock().Header.ParentHashes
if len(parentHashes) > 0 {
if !bi.dag.HaveBlocks(parentHashes) {
if !bi.dag.AreKnownBlocks(parentHashes) {
return false, errors.Errorf("import file contains block "+
"%v which does not link to the available "+
"block DAG", parentHashes)

View File

@ -591,7 +591,7 @@ func (sm *SyncManager) haveInventory(invVect *wire.InvVect) (bool, error) {
fallthrough
case wire.InvTypeBlock:
// Ask DAG if the block is known to it in any form (in DAG or as an orphan).
return sm.dag.HaveBlock(invVect.Hash), nil
return sm.dag.IsKnownBlock(invVect.Hash), nil
case wire.InvTypeTx:
// Ask the transaction memory pool if the transaction is known

View File

@ -198,9 +198,9 @@ type Config struct {
// to the peer as needed.
SelectedTipHash func() *daghash.Hash
// BlockExists determines whether a block with the given hash exists in
// IsInDAG determines whether a block with the given hash exists in
// the DAG.
BlockExists func(*daghash.Hash) bool
IsInDAG func(*daghash.Hash) bool
// HostToNetAddress returns the netaddress for the given host. This can be
// nil in which case the host will be parsed as an IP address.
@ -662,7 +662,7 @@ func (p *Peer) SetSelectedTipHash(selectedTipHash *daghash.Hash) {
//
// This function is safe for concurrent access.
func (p *Peer) IsSelectedTipKnown() bool {
return !p.cfg.BlockExists(p.selectedTipHash)
return !p.cfg.IsInDAG(p.selectedTipHash)
}
// LastSend returns the last send time of the peer.

View File

@ -21,7 +21,7 @@ func (sp *Peer) OnBlockLocator(_ *peer.Peer, msg *wire.MsgBlockLocator) {
// If the first hash of the block locator is known, it means we found
// the highest shared block.
highHash := msg.BlockLocatorHashes[0]
if dag.BlockExists(highHash) {
if dag.IsInDAG(highHash) {
if dag.IsKnownFinalizedBlock(highHash) {
peerLog.Debugf("Cannot sync with peer %s because the highest"+
" shared chain block (%s) is below the finality point", sp, highHash)

View File

@ -304,7 +304,7 @@ func (sp *Peer) selectedTipHash() *daghash.Hash {
// blockExists determines whether a block with the given hash exists in
// the DAG.
func (sp *Peer) blockExists(hash *daghash.Hash) bool {
return sp.server.DAG.BlockExists(hash)
return sp.server.DAG.IsInDAG(hash)
}
// addKnownAddresses adds the given addresses to the set of known addresses to
@ -1052,7 +1052,7 @@ func newPeerConfig(sp *Peer) *peer.Config {
OnWrite: sp.OnWrite,
},
SelectedTipHash: sp.selectedTipHash,
BlockExists: sp.blockExists,
IsInDAG: sp.blockExists,
HostToNetAddress: sp.server.addrManager.HostToNetAddress,
Proxy: config.ActiveConfig().Proxy,
UserAgentName: userAgentName,

View File

@ -29,7 +29,7 @@ func handleGetBlocks(s *Server, cmd interface{}, closeChan <-chan struct{}) (int
defer s.cfg.DAG.RUnlock()
// If lowHash is not in the DAG, there's nothing to do; return an error.
if lowHash != nil && !s.cfg.DAG.HaveBlock(lowHash) {
if lowHash != nil && !s.cfg.DAG.IsKnownBlock(lowHash) {
return nil, &rpcmodel.RPCError{
Code: rpcmodel.ErrRPCBlockNotFound,
Message: "Block not found",

View File

@ -38,7 +38,7 @@ func handleGetChainFromBlock(s *Server, cmd interface{}, closeChan <-chan struct
// If startHash is not in the selected parent chain, there's nothing
// to do; return an error.
if startHash != nil && !s.cfg.DAG.BlockExists(startHash) {
if startHash != nil && !s.cfg.DAG.IsInDAG(startHash) {
return nil, &rpcmodel.RPCError{
Code: rpcmodel.ErrRPCBlockNotFound,
Message: "Block not found in the DAG",