mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-09-13 04:50:11 +00:00

* [NOD-1249] Add pruning related constants (#869) * [NOD-1249] Add pruning related constants * [NOD-1249] Change status suspect to UTXONotVerified * [NOD-1249] Add TestPruningDepth * [NOD-1249] Add comment to pruningDepth * [NOD-1249] Add pruning helper functions (#875) * [NOD-1249] Added node.blockAtDepth * [NOD-1249] Added node.finalityPoint() * [NOD-1249] Add hasFinalityPointInOthersSelectedChain * [NOD-1249] Add nonFinalityViolatingBlues * [NOD-1249] Added isInPastOfAny * [NOD-1249] Updated all calls to blockNode functions that require dag * [NOD-1249] Add blockNode.reds field and persist it * [NOD-1249] Add checkObjectiveFinality * [NOD-1249] Add isViolatingSubjectiveFinality * [NOD-1249] Added to TestGHOSTDAG check that reds are as expected * [NOD-1249] Add checkMergeLimit and checkDAGRelations * [NOD-1249] Invert condition in blockInDepth * [NOD-1249] Make isInPastOfAny resemble isInPast * [NOD-1249] Added comments to isInPast and isInPastOfAny * [NOD-1252] Remove any references to legacy finality (#876) * [NOD-1032] validateParents: check number of parents and that no parents were manually rejected (#877) * [NOD-1254] Block verification changes (#882) * [NOD-1254] Call checkDAGRelations and move it to correct place * [NOD-1254] Use blockStatuses properly * [NOD-1254] Add support for setting node's verification flag and set it to UTXONotVerified once block passes basic verification * [NOD-1254] Check for subjctiveFinality, and for node not being in the selectedParentChain * [NOD-1254] Make blockStatus an ordinary value - not bit flags * [NOD-1254] Isolate all utxo-requiring validation into a single separate if branches * [NOD-1254] Re-arrange connectBloc so that things that happen in UTXO-validated blocks only are all grouped together * [NOD-1254] Resolve and check selectedParent's status before validatingUTXO * [NOD-1254] Separate virtualUTXODiff from utxoVerificationOutput * [NOD-1254] Stylistic fixes * [NOD-1254] Use dag.index.(Set)BlockNodeStatus instead of accessing node.status * [NOD-1288] Sub-routinize checkConnectToPastUTXO * [NOD-1288] Re-write checkConnectToPastUTXO in a way that allows to filter-out invalid transactions * [NOD-1288] Make checkTxSequenceLock use already calculated utxo outputs * [NOD-1288] Make checkTxMass use already calculated utxo outputs * [NOD-1288] Use dag.sigCache for ValidateTransactionScripts * [NOD-1288] Use checkConnectTransactionToPastUTXO in applyBlueBlocks * [NOD-1288] Clean-up old code-path from no longer used functions * [NOD-1288] Skip any irrelevant parts of txo verification if block is genesis * [NOD-1288] Set where it should have been * [NOD-1288] Fix reachability checks to never use the new node + make isInSelectedParentChainOf return true if node == other * [NOD-1288] invert the condition for isNewSelectedTip * [NOD-1288] Separate checkIsAccepted to own function, and properly handle coinbase * [NOD-1288] Don't update utxo-diff for UTXONotVerified parents/tips + Make PrepareBlockForTest resolve the selectedParent's UTXOSet if needed * [NOD-1288] Include mass off coinbase transactions * [NOD-1288] Move comment to correct position * [NOD-1288] If blockAtDepth should return genesis - do it immidiately * [NOD-1288] Comment-out instead of removeing scriptval_test.go * [NOD-1288] Rename: entry -> utxoEntry * [NOD-1288] Remove special function for calcCoinbaseTxMass * [NOD-1288] Remove redundant check from checkEntryAmounts * [NOD-1288] Rename: MaxMassPerBlock -> MaxMassAcceptedByBlock * [NOD-1255] Implement boundedMergeBreakingParents * [NOD-1255] Implement selectAllowedTips * [NOD-1255] Integrate virtual parent selection into block verification process * [NOD-1255] Add node to tips all the time, remove it from candidates and add it's parents if it's disqualified * [NOD-1255] remove tips from virtaulBlock * [NOD-1255] Rename: didVirtualParentsChanged -> didVirtualParentsChange * [NOD-1255] Remove redundant sanity check * [NOD-1255] Handle a forgotten error * [NOD-1255] Prettify selectVirtualParents * [NOD-1255] UpdateTipsUTXO should run over all UTXO-Verified tips, even if they are not parents of virtual * [NOD-1311] Make isInPast inclusive * [NOD-1032] Handle finality conflicts (#904) * [NOD-1312] AddTip should not include finalityViolating and manuallyRejected blocks * [NOD-1312] Implement resolveFinalityConflict * [NOD-1312] Implement dag notifications for finalityChanges + updateing DAG state * [NOD-1312] Added finality conflict rpc boilerplate * [NOD-1312] Implement handling of getFinalityConflicts + resolveFinalityConflict RPCs * [NOD-1312] Implement finality conflict related notifications * [NOD-1312] Move all time to millisecond time * [NOD-1312] Add comments + unexport some methods * [NOD-1312] Add clarification in comments * [NOD-1312] Move updateFinalityConflictResolution to finality_conflicts.go * [NOD-1312] Rename: currentSelectedTip -> selectedTip * [NOD-1312] Add long comment to ResolveFinalityConflict * [NOD-1312] Convert areAllInSelectedParentChainOf into isInSelectedParentChainOfAll * [NOD-1312] Rename chainUpdates (type) -> selectedParentChainUpdates, to distinguish from the variable chainUpdates * [NOD-1032] Make all blockdag tests compile * [NOD-1278] Fix finality-related tests (#910) * [NOD-1032] Don't return node.dag.genesis from blockAtDepth because it might still not exist * [NOD-1032] Actually add a tip in dag.addTip * [NOD-1278] Add transaction to utxo set if it's coinbase * [NOD-1278] Use VirtualParentHashes instead of TipHashes where appropriate * [NOD-1278] If no valid virtual parent candidates - return error, don't wait for panic * [NOD-1278] Transition TestCalcSequenceLock from newTestDAG to DAGSetup * [NOD-1278] Fix .bluest() tie-breaker * [NOD-1278] Remove feeData structure, as it no longer works, store feeData in acceptanceData * [NOD-1278] Remove dag parameter from blockNode methods * [NOD-1278] Fix TestBlueBlockWindow * [NOD-1278] Don't subject selectedParent to MaxMergeSet * [NOD-1278] se PrepareAndProcessBlockForTest instead of .addTip in TestSelectedPath * [NOD-1278] Fixed TestDAGStateSerialization * [NOD-1278] Fix TestAcceptanceIndexRecover * [NOD-1278] Fix TestCheckConnectBlockTemplate * [NOD-1278] Fix TestChainUpdates * [NOD-1278] Fix and rename TestVirtualBlock -> TestTips * [NOD-1278] Rename checkIsAccepted -> maybeAcceptTx * [NOD-1278] Re-activate TestDoubleSpends * Revert "[NOD-1278] Fixed TestDAGStateSerialization" This reverts commit 845095d6de7207b07cf819d05f3f38ad94da9cf6. * [NOD-1278] Remove dag parameter from expectedCoinbaseTransaction * [NOD-1348] Implemented simplified Finality Conflict Resolution scheme (#911) * [NOD-1348] Rename functions according to Research spec * [NOD-1348] Added blockSet.areAllIn * [NOD-1348] Implemented simplified finality conflict resolution scheme * [NOD-1348] Refactorings and fixes in selectVirtualParents * [NOD-1278] Fix bugs discovered by unit-tests + Fix unit-tests (#916) * Updated to version v0.3.1 * [NOD-858] Don't switch sync peer if the syncing process hasn't yet started with the current sync peer (#700) * [NOD-858] Don't switch sync peer if the syncing process hasn't yet started with the current sync peer * [NOD-858] SetShouldSendBlockLocator(false) on OnBlockLocator * [NOD-858] Rename shouldSendBlockLocator->wasBlockLocatorRequested * [NOD-858] Move panic to shouldReplaceSyncPeer * [NOD-869] Add a print after os.Exit(1) to see if it is ever called (#701) * [NOD-1238] Fix acceptance index never being initialized. (#859) * [NOD-1278] Genesis never violates finality * [NOD-1348] Refactorings and fixes in selectVirtualParents * [NOD-1278] Don't call dag.selectVirtualParents for genesis * [NOD-1278] Properly organize errors in maybeAcceptBlock * [NOD-1278] updateTipsUTXO should only run on tips whose status is * [NOD-1278] updateTipsUTXO should only run on tips whose status is `valid` * [NOD-1278] Fix TestDoubleSpends * [NOD-1278] Fix TestDAGIndexFailedStatus * [NOD-1278] IsFinalizedTransaction should use uint64 everywhere * [NOD-1278] If tx is coinbase and not selectedParent - don't update pastUTXO * [NOD-1278] Store tips and validTips separately * [NOD-1278] Store ValidTips and VirtualParents in dagState * [NOD-1278] Fix TestProcessOrphans * [NOD-1278] Fix TestProcessOrphans * [NOD-1278] Fix TestOrderInDiffFromAcceptanceData * [NOD-1278] Fix TestHelp * [NOD-1278] Remove mining.PrepareBlockForTest; use blockdag.PrepareBlockForTest instead * [NOD-1278] Explicitly disallow chained transactions * [NOD-1278] * [NOD-1278] Fix some comments Co-authored-by: Ori Newman <orinewman1@gmail.com> Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com> Co-authored-by: Yuval Shaul <yuval.shaul@gmail.com> * [NOD-1355] Add unit-test for finality + When resolving finalityConflict - make sure the block that will come out selectedTip is statusValid (#919) * [NOD-1355] Added test for finality * [NOD-1355] When resolving finalityConflict - make sure the block that will come out selectedTip is statusValid * [NOD-1032] Renames: anything about inputsWithReferencedUTXOEntries -> remove 'Referenced' * [NOD-1032] Don't ignore non-rule errors * [NOD-1032] Fix comment * [NOD-1032] Enhanced comments on TestChainUpdates * [NOD-1032] Remove scriptval_test.go * [NOD-1032] Extracted isNewSelectedTip to a method * [NOD-1032] Use dag.Now() instead of mstime.Now() * [NOD-1032] Print block status when accepting block * [NOD-1032] Add comment explaining boundedMergeBreakingParents * [NOD-1032] Enhanced test and imporved comments in TestFinality * [NOD-1032] Rename: Objective finality -> bounded merge depth * [NOD-1032] No need to check that validTips are valid * [NOD-1032] Remove dag from arguments of updateDiffAndDiffChild * [NOD-1032] Correct variable names in LookupNodes [NOD-1032] Correct variable names in LookupNodes * [NOD-1032] Fix some comments * [NOD-1032] Some style changes * [NOD-1032] Refactor isAnyInPastOf * [NOD-1032] Enhance comment in updateVirtualParents * [NOD-1032] Flip condition in updateVirtualParents * [NOD-1032] Stylistic and grammatic fixes in dag.go and dag_test.go * [NOD-1032] Explain why updateSelectedParentSet receives geneses on init * [NOD-1032] Remove ErrParentManuallyRejected * [NOD-1032] Added wrapper for resolveNodeStatus that creates a special transaction for it * [NOD-1032] Rename: statusUTXONotVerified -> statusUTXOPendingVerification * [NOD-1032] Use virtual parents in CurrentBits() * [NOD-1032] rename: isViolatingSubjectiveFinality -> isViolatingFinality * [NOD-1032] Move updateVirtualAndTips to applyDAGChanges * [NOD-1032] Invert condition for isFinalityPointInPast * [NOD-1032] Fix antiPastBetween isInPast became inclusive * [NOD-1032] Remove redundant call for addTip * [NOD-1032] Use calcCoinbaseTxMass where appropriate * [NOD-1032] Remove time fields from conflict notifications * [NOD-1032] Assign the correct thing to i * [NOD-1032] unify checkOutputsAmounts and checkTxOutputAmounts * [NOD-1032] Cleanup in CheckTransactionInputsAndCalulateFee * [NOD-1032] Fixed some style and comments * [NOD-1032] If selectedParent is disqualifiedFromChain - validateAndApplyUTXOSet should return this as a ruleError * [NOD-1032] Set the status in resolveNodeStatus * [NOD-1032] Correct comment on boundedMergeBreakingParents * [NOD-1032] Fix a typo. * [NOD-1032] Update a variable name. * [NOD-1032] Fix a comment. * [NOD-1032] Fix merge errors. * [NOD-1032] Add VirtualParentHashes to getBlockDagInfo. * [NOD-1032] Update handleGetBlockTemplate. * [NOD-1032] Comment out all the old RPC stuff. * [NOD-1032] Remove irrelevant type. * [NOD-1032] Implement ResolveFinalityConflict. * [NOD-1032] Remove irrelevant comments. * [NOD-1032] Implement NotifyFinalityConflicts. * [NOD-1032] Add FinalityConflictNotification and FinalityConflictResolvedNotification. * [NOD-1032] Finish implementing finality conflict notifications. * [NOD-1032] Remove old RPC stuff. * [NOD-1032] Fix grammar in a comment. Co-authored-by: Ori Newman <orinewman1@gmail.com> Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com> Co-authored-by: Yuval Shaul <yuval.shaul@gmail.com> Co-authored-by: stasatdaglabs <stas@daglabs.com>
250 lines
8.3 KiB
Go
250 lines
8.3 KiB
Go
package blockdag
|
|
|
|
import (
|
|
"github.com/kaspanet/kaspad/app/appmessage"
|
|
"github.com/kaspanet/kaspad/util/daghash"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// BlockLocator is used to help locate a specific block. The algorithm for
|
|
// building the block locator is to add block hashes in reverse order on the
|
|
// block's selected parent chain until the desired stop block is reached.
|
|
// In order to keep the list of locator hashes to a reasonable number of entries,
|
|
// the step between each entry is doubled each loop iteration to exponentially
|
|
// decrease the number of hashes as a function of the distance from the block
|
|
// being located.
|
|
//
|
|
// For example, assume a selected parent chain with IDs as depicted below, and the
|
|
// stop block is genesis:
|
|
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
|
//
|
|
// The block locator for block 17 would be the hashes of blocks:
|
|
// [17 16 14 11 7 2 genesis]
|
|
type BlockLocator []*daghash.Hash
|
|
|
|
// BlockLocatorFromHashes returns a block locator from high and low hash.
|
|
// See BlockLocator for details on the algorithm used to create a block locator.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (dag *BlockDAG) BlockLocatorFromHashes(highHash, lowHash *daghash.Hash) (BlockLocator, error) {
|
|
dag.dagLock.RLock()
|
|
defer dag.dagLock.RUnlock()
|
|
|
|
highNode, ok := dag.index.LookupNode(highHash)
|
|
if !ok {
|
|
return nil, errors.Errorf("block %s is unknown", highHash)
|
|
}
|
|
|
|
lowNode, ok := dag.index.LookupNode(lowHash)
|
|
if !ok {
|
|
return nil, errors.Errorf("block %s is unknown", lowHash)
|
|
}
|
|
|
|
return dag.blockLocator(highNode, lowNode)
|
|
}
|
|
|
|
// blockLocator returns a block locator for the passed high and low nodes.
|
|
// See the BlockLocator type comments for more details.
|
|
//
|
|
// This function MUST be called with the DAG state lock held (for reads).
|
|
func (dag *BlockDAG) blockLocator(highNode, lowNode *blockNode) (BlockLocator, error) {
|
|
// We use the selected parent of the high node, so the
|
|
// block locator won't contain the high node.
|
|
highNode = highNode.selectedParent
|
|
|
|
node := highNode
|
|
step := uint64(1)
|
|
locator := make(BlockLocator, 0)
|
|
for node != nil {
|
|
locator = append(locator, node.hash)
|
|
|
|
// Nothing more to add once the low node has been added.
|
|
if node.blueScore <= lowNode.blueScore {
|
|
if node != lowNode {
|
|
return nil, errors.Errorf("highNode and lowNode are " +
|
|
"not in the same selected parent chain.")
|
|
}
|
|
break
|
|
}
|
|
|
|
// Calculate blueScore of previous node to include ensuring the
|
|
// final node is lowNode.
|
|
nextBlueScore := node.blueScore - step
|
|
if nextBlueScore < lowNode.blueScore {
|
|
nextBlueScore = lowNode.blueScore
|
|
}
|
|
|
|
// walk backwards through the nodes to the correct ancestor.
|
|
node = node.SelectedAncestor(nextBlueScore)
|
|
|
|
// Double the distance between included hashes.
|
|
step *= 2
|
|
}
|
|
|
|
return locator, nil
|
|
}
|
|
|
|
// FindNextLocatorBoundaries returns the lowest unknown block locator, hash
|
|
// and the highest known block locator hash. This is used to create the
|
|
// next block locator to find the highest shared known chain block with the
|
|
// sync peer.
|
|
//
|
|
// This function MUST be called with the DAG state lock held (for reads).
|
|
func (dag *BlockDAG) FindNextLocatorBoundaries(locator BlockLocator) (highHash, lowHash *daghash.Hash) {
|
|
// Find the most recent locator block hash in the DAG. In the case none of
|
|
// the hashes in the locator are in the DAG, fall back to the genesis block.
|
|
lowNode := dag.genesis
|
|
nextBlockLocatorIndex := int64(len(locator) - 1)
|
|
for i, hash := range locator {
|
|
node, ok := dag.index.LookupNode(hash)
|
|
if ok {
|
|
lowNode = node
|
|
nextBlockLocatorIndex = int64(i) - 1
|
|
break
|
|
}
|
|
}
|
|
if nextBlockLocatorIndex < 0 {
|
|
return nil, lowNode.hash
|
|
}
|
|
return locator[nextBlockLocatorIndex], lowNode.hash
|
|
}
|
|
|
|
// antiPastHashesBetween returns the hashes of the blocks between the
|
|
// lowHash's antiPast and highHash's antiPast, or up to the provided
|
|
// max number of block hashes.
|
|
//
|
|
// This function MUST be called with the DAG state lock held (for reads).
|
|
func (dag *BlockDAG) antiPastHashesBetween(lowHash, highHash *daghash.Hash, maxHashes uint64) ([]*daghash.Hash, error) {
|
|
nodes, err := dag.antiPastBetween(lowHash, highHash, maxHashes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
hashes := make([]*daghash.Hash, len(nodes))
|
|
for i, node := range nodes {
|
|
hashes[i] = node.hash
|
|
}
|
|
return hashes, nil
|
|
}
|
|
|
|
// antiPastBetween returns the blockNodes between the lowHash's antiPast
|
|
// and highHash's antiPast, or up to the provided max number of blocks.
|
|
//
|
|
// This function MUST be called with the DAG state lock held (for reads).
|
|
func (dag *BlockDAG) antiPastBetween(lowHash, highHash *daghash.Hash, maxEntries uint64) ([]*blockNode, error) {
|
|
lowNode, ok := dag.index.LookupNode(lowHash)
|
|
if !ok {
|
|
return nil, errors.Errorf("couldn't find low hash %s", lowHash)
|
|
}
|
|
highNode, ok := dag.index.LookupNode(highHash)
|
|
if !ok {
|
|
return nil, errors.Errorf("couldn't find high hash %s", highHash)
|
|
}
|
|
if lowNode.blueScore >= highNode.blueScore {
|
|
return nil, errors.Errorf("low hash blueScore >= high hash blueScore (%d >= %d)",
|
|
lowNode.blueScore, highNode.blueScore)
|
|
}
|
|
|
|
// In order to get no more then maxEntries blocks from the
|
|
// future of the lowNode (including itself), we iterate the
|
|
// selected parent chain of the highNode and stop once we reach
|
|
// highNode.blueScore-lowNode.blueScore+1 <= maxEntries. That
|
|
// stop point becomes the new highNode.
|
|
// Using blueScore as an approximation is considered to be
|
|
// fairly accurate because we presume that most DAG blocks are
|
|
// blue.
|
|
for highNode.blueScore-lowNode.blueScore+1 > maxEntries {
|
|
highNode = highNode.selectedParent
|
|
}
|
|
|
|
// Collect every node in highNode's past (including itself) but
|
|
// NOT in the lowNode's past (excluding itself) into an up-heap
|
|
// (a heap sorted by blueScore from lowest to greatest).
|
|
visited := newBlockSet()
|
|
candidateNodes := newUpHeap()
|
|
queue := newDownHeap()
|
|
queue.Push(highNode)
|
|
for queue.Len() > 0 {
|
|
current := queue.pop()
|
|
if visited.contains(current) {
|
|
continue
|
|
}
|
|
visited.add(current)
|
|
var isCurrentAncestorOfLowNode bool
|
|
if current == lowNode {
|
|
isCurrentAncestorOfLowNode = false
|
|
} else {
|
|
var err error
|
|
isCurrentAncestorOfLowNode, err = dag.isInPast(current, lowNode)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if isCurrentAncestorOfLowNode {
|
|
continue
|
|
}
|
|
candidateNodes.Push(current)
|
|
for parent := range current.parents {
|
|
queue.Push(parent)
|
|
}
|
|
}
|
|
|
|
// Pop candidateNodes into a slice. Since candidateNodes is
|
|
// an up-heap, it's guaranteed to be ordered from low to high
|
|
nodesLen := int(maxEntries)
|
|
if candidateNodes.Len() < nodesLen {
|
|
nodesLen = candidateNodes.Len()
|
|
}
|
|
nodes := make([]*blockNode, nodesLen)
|
|
for i := 0; i < nodesLen; i++ {
|
|
nodes[i] = candidateNodes.pop()
|
|
}
|
|
return nodes, nil
|
|
}
|
|
|
|
// AntiPastHashesBetween returns the hashes of the blocks between the
|
|
// lowHash's antiPast and highHash's antiPast, or up to the provided
|
|
// max number of block hashes.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (dag *BlockDAG) AntiPastHashesBetween(lowHash, highHash *daghash.Hash, maxHashes uint64) ([]*daghash.Hash, error) {
|
|
dag.dagLock.RLock()
|
|
defer dag.dagLock.RUnlock()
|
|
hashes, err := dag.antiPastHashesBetween(lowHash, highHash, maxHashes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return hashes, nil
|
|
}
|
|
|
|
// antiPastHeadersBetween returns the headers of the blocks between the
|
|
// lowHash's antiPast and highHash's antiPast, or up to the provided
|
|
// max number of block headers.
|
|
//
|
|
// This function MUST be called with the DAG state lock held (for reads).
|
|
func (dag *BlockDAG) antiPastHeadersBetween(lowHash, highHash *daghash.Hash, maxHeaders uint64) ([]*appmessage.BlockHeader, error) {
|
|
nodes, err := dag.antiPastBetween(lowHash, highHash, maxHeaders)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
headers := make([]*appmessage.BlockHeader, len(nodes))
|
|
for i, node := range nodes {
|
|
headers[i] = node.Header()
|
|
}
|
|
return headers, nil
|
|
}
|
|
|
|
// AntiPastHeadersBetween returns the headers of the blocks between the
|
|
// lowHash's antiPast and highHash's antiPast, or up to
|
|
// appmessage.MaxBlockHeadersPerMsg block headers.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (dag *BlockDAG) AntiPastHeadersBetween(lowHash, highHash *daghash.Hash, maxHeaders uint64) ([]*appmessage.BlockHeader, error) {
|
|
dag.dagLock.RLock()
|
|
defer dag.dagLock.RUnlock()
|
|
headers, err := dag.antiPastHeadersBetween(lowHash, highHash, maxHeaders)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return headers, nil
|
|
}
|