In IBD, validate the timestamps of the headers of the pruning point and selected tip (#1829)

* Implement validatePruningPointFutureHeaderTimestamps.

* Fix TestIBDWithPruning.

* Fix wrong logic.

* Add a comment.

* Fix a comment.

* Fix a variable name.

* Add a commment

* Fix TestIBDWithPruning.

Co-authored-by: Ori Newman <orinewman1@gmail.com>
This commit is contained in:
stasatdaglabs 2021-10-30 20:32:49 +03:00 committed by GitHub
parent 3dbc42b4f7
commit 77a344cc29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 89 additions and 8 deletions

View File

@ -320,6 +320,40 @@ func (flow *handleRelayInvsFlow) processHeader(consensus externalapi.Consensus,
return nil
}
func (flow *handleRelayInvsFlow) validatePruningPointFutureHeaderTimestamps() error {
headerSelectedTipHash, err := flow.Domain().StagingConsensus().GetHeadersSelectedTip()
if err != nil {
return err
}
headerSelectedTipHeader, err := flow.Domain().StagingConsensus().GetBlockHeader(headerSelectedTipHash)
if err != nil {
return err
}
headerSelectedTipTimestamp := headerSelectedTipHeader.TimeInMilliseconds()
currentSelectedTipHash, err := flow.Domain().Consensus().GetHeadersSelectedTip()
if err != nil {
return err
}
currentSelectedTipHeader, err := flow.Domain().Consensus().GetBlockHeader(currentSelectedTipHash)
if err != nil {
return err
}
currentSelectedTipTimestamp := currentSelectedTipHeader.TimeInMilliseconds()
if headerSelectedTipTimestamp < currentSelectedTipTimestamp {
return protocolerrors.Errorf(false, "the timestamp of the candidate selected "+
"tip is smaller than the current selected tip")
}
minTimestampDifferenceInMilliseconds := (10 * time.Minute).Milliseconds()
if headerSelectedTipTimestamp-currentSelectedTipTimestamp < minTimestampDifferenceInMilliseconds {
return protocolerrors.Errorf(false, "difference between the timestamps of "+
"the current pruning point and the candidate pruning point is too small. Aborting IBD...")
}
return nil
}
func (flow *handleRelayInvsFlow) receiveAndInsertPruningPointUTXOSet(
consensus externalapi.Consensus, pruningPointHash *externalapi.DomainHash) (bool, error) {

View File

@ -137,6 +137,11 @@ func (flow *handleRelayInvsFlow) downloadHeadersAndPruningUTXOSet(highHash *exte
return protocolerrors.Errorf(true, "the triggering IBD block was not sent")
}
err = flow.validatePruningPointFutureHeaderTimestamps()
if err != nil {
return err
}
log.Debugf("Syncing the current pruning point UTXO set")
syncedPruningPointUTXOSetSuccessfully, err := flow.syncPruningPointUTXOSet(flow.Domain().StagingConsensus(), proofPruningPoint)
if err != nil {

View File

@ -54,15 +54,15 @@ type Config struct {
// DefaultConfig returns the default mempool configuration
func DefaultConfig(dagParams *dagconfig.Params) *Config {
targetBlocksPerSecond := uint64(time.Second / dagParams.TargetTimePerBlock)
targetBlocksPerSecond := time.Second.Seconds() / dagParams.TargetTimePerBlock.Seconds()
return &Config{
MaximumTransactionCount: defaultMaximumTransactionCount,
TransactionExpireIntervalDAAScore: defaultTransactionExpireIntervalSeconds / targetBlocksPerSecond,
TransactionExpireScanIntervalDAAScore: defaultTransactionExpireScanIntervalSeconds / targetBlocksPerSecond,
TransactionExpireIntervalDAAScore: uint64(float64(defaultTransactionExpireIntervalSeconds) / targetBlocksPerSecond),
TransactionExpireScanIntervalDAAScore: uint64(float64(defaultTransactionExpireScanIntervalSeconds) / targetBlocksPerSecond),
TransactionExpireScanIntervalSeconds: defaultTransactionExpireScanIntervalSeconds,
OrphanExpireIntervalDAAScore: defaultOrphanExpireIntervalSeconds / targetBlocksPerSecond,
OrphanExpireScanIntervalDAAScore: defaultOrphanExpireScanIntervalSeconds / targetBlocksPerSecond,
OrphanExpireIntervalDAAScore: uint64(float64(defaultOrphanExpireIntervalSeconds) / targetBlocksPerSecond),
OrphanExpireScanIntervalDAAScore: uint64(float64(defaultOrphanExpireScanIntervalSeconds) / targetBlocksPerSecond),
MaximumOrphanTransactionMass: defaultMaximumOrphanTransactionMass,
MaximumOrphanTransactionCount: defaultMaximumOrphanTransactionCount,
AcceptNonStandard: dagParams.RelayNonStdTxs,

View File

@ -1,6 +1,7 @@
package integration
import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"reflect"
"sync"
@ -116,7 +117,7 @@ func TestIBDWithPruning(t *testing.T) {
}
// This should trigger resolving the syncee virtual
syncerTip := mineNextBlock(t, syncer)
syncerTip := mineNextBlockWithMockTimestamps(t, syncer)
time.Sleep(time.Second)
synceeSelectedTip, err := syncee.rpcClient.GetSelectedTipHash()
if err != nil {
@ -132,6 +133,12 @@ func TestIBDWithPruning(t *testing.T) {
overrideDAGParams := dagconfig.SimnetParams
// Increase the target time per block so that we could mine
// blocks with timestamps that are spaced far enough apart
// to avoid failing the timestamp threshold validation of
// ibd-with-headers-proof
overrideDAGParams.TargetTimePerBlock = time.Minute
// This is done to make a pruning depth of 6 blocks
overrideDAGParams.FinalityDuration = 2 * overrideDAGParams.TargetTimePerBlock
overrideDAGParams.K = 0
@ -178,11 +185,11 @@ func TestIBDWithPruning(t *testing.T) {
// block.
const synceeOnlyBlocks = 2
for i := 0; i < synceeOnlyBlocks; i++ {
mineNextBlock(t, syncee1)
mineNextBlockWithMockTimestamps(t, syncee1)
}
for i := 0; i < numBlocks-1; i++ {
mineNextBlock(t, syncer)
mineNextBlockWithMockTimestamps(t, syncer)
}
testSync(syncer, syncee1)
@ -190,3 +197,38 @@ func TestIBDWithPruning(t *testing.T) {
// Test a situation where a node with pruned headers syncs another fresh node.
testSync(syncee1, syncee2)
}
var currentMockTimestamp int64 = 0
// mineNextBlockWithMockTimestamps mines blocks with large timestamp differences
// between every two blocks. This is done to avoid the timestamp threshold validation
// of ibd-with-headers-proof
func mineNextBlockWithMockTimestamps(t *testing.T, harness *appHarness) *externalapi.DomainBlock {
blockTemplate, err := harness.rpcClient.GetBlockTemplate(harness.miningAddress)
if err != nil {
t.Fatalf("Error getting block template: %+v", err)
}
block, err := appmessage.RPCBlockToDomainBlock(blockTemplate.Block)
if err != nil {
t.Fatalf("Error converting block: %s", err)
}
if currentMockTimestamp == 0 {
currentMockTimestamp = block.Header.TimeInMilliseconds()
} else {
currentMockTimestamp += 10_000
}
mutableHeader := block.Header.ToMutable()
mutableHeader.SetTimeInMilliseconds(currentMockTimestamp)
block.Header = mutableHeader.ToImmutable()
solveBlock(block)
_, err = harness.rpcClient.SubmitBlock(block)
if err != nil {
t.Fatalf("Error submitting block: %s", err)
}
return block
}