Compare commits

..

91 Commits

Author SHA1 Message Date
oudeis
16e434aa91 Fixed version 2020-11-29 05:25:27 +00:00
Ori Newman
0e91b44fc6 [NOD-1577] Change cache size to 200 (#1156) 2020-11-26 17:11:49 +02:00
Svarog
f7fa823f17 [NOD-1551] Requirements for performance tests (#1154)
* [NOD-1551] Add NewTestConsensusWithDataDir to factory

* [NOD-1551] Cache transaction ID

* [NOD-1551] Should return err if err != nil

* [NOD-1551] BuildBlockWithParents returns the blocks pastUTXOData

* [NOD-1551] Set BlockCoinbaseMaturity to 0 in TestDoubleSpends

* [NOD-1551] Fix comments

* --amend

Co-authored-by: Ori Newman <orinewman1@gmail.com>
2020-11-26 12:12:01 +02:00
Svarog
546ea83123 [NOD-1570] Fix the way UTXO iterators work (#1153)
* [NOD-1570] Implement utxo.IteratorWithDiff

* [NOD-1570] Utilize utxo.ITeratorWithDiff in RestorePastUTXOSetIterator and VirtualUTXOSetIterator

* [NOD-1570] Fix comment
2020-11-25 18:28:42 +02:00
Elichai Turkel
f9c2137344 [RES-65] Add a test for BoundedMergeDepth - new (#1131)
* Test bounded merge depth

* Fix a bug in GetBlockInfo, where trying to use reachability on an invalid block

* Add a test to reproduce and test the GetBlockInfo bug
2020-11-25 13:42:55 +02:00
stasatdaglabs
0fa13357c3 [NOD-1566] Add caching to all stores (#1152)
* [NOD-1566] Add a dependency to golang-lru.

* [NOD-1566] Add caching to blockstore.go.

* [NOD-1566] Add LRUCache to all store objects and initialize them.

* [NOD-1566] Add caching to acceptanceDataStore.

* [NOD-1566] Add caching to blockHeaderStore.

* [NOD-1566] Implement a simpler LRU cache.

* [NOD-1566] Use the simpler cache implementation everywhere.

* [NOD-1566] Remove dependency in golang-lru.

* [NOD-1566] Fix object reuse issues in store Get functions.

* [NOD-1566] Add caching to blockRelationStore.

* [NOD-1566] Add caching to blockStatusStore.

* [NOD-1566] Add caching to ghostdagDataStore.

* [NOD-1566] Add caching to multisetStore.

* [NOD-1566] Add caching to reachabilityDataStore.

* [NOD-1566] Add caching to utxoDiffStore.

* [NOD-1566] Add caching to reachabilityReindexRoot.

* [NOD-1566] Add caching to pruningStore.

* [NOD-1566] Add caching to headerTipsStore.

* [NOD-1566] Add caching to consensusStateStore.

* [NOD-1566] Add comments explaining why we don't discard staging at the normal location in consensusStateStore.

* [NOD-1566] Make go vet happy.

* [NOD-1566] Fix merge errors.

* [NOD-1566] Add a missing break statement.

* [NOD-1566] Run go mod tidy.

* [NOD-1566] Remove serializedUTXOSetCache.
2020-11-25 13:41:13 +02:00
Ori Newman
5b2fae0457 [NOD-1568] Add staticcheck checks (#1150) 2020-11-25 11:43:51 +02:00
Ori Newman
3bad9ec1eb [NOD-1569] Stop using ReceiveFromChanWhenDone (#1151) 2020-11-25 11:30:07 +02:00
Ori Newman
45d9b63572 [NOD-1567] Add clone methods to data stores types (#1149)
* [NOD-1567] Add clone methods to data stores types

* [NOD-1567] Fix comments

* [NOD-1567] Fix test
2020-11-24 17:56:18 +02:00
Elichai Turkel
afc634d871 Add TestCheckBlockSanity back (#1137) 2020-11-24 16:57:40 +02:00
Ori Newman
2334f8b4eb [NOD-1564] Add TestChainedTransactions (#1145)
* [NOD-1564] Add TestChainedTransactions

* [NOD-1564] Fix errors
2020-11-24 11:42:53 +02:00
stasatdaglabs
d65f382c80 [NOD-1565] Reorder getSyncInfo in a way that won't unnecessarily call HeaderTipsPruningPoint. (#1146) 2020-11-24 11:34:02 +02:00
Ori Newman
2096a28d1c [NOD-1563] Add TestMaxHeaders (#1144) 2020-11-23 18:33:45 +02:00
Ori Newman
96d9e5800f [NOD-1561] Add TestCheckParentsIncest and fix validation order (#1143) 2020-11-23 18:27:44 +02:00
Ori Newman
8264369c81 [NOD-1561] Add TestValidateMedianTime (#1141)
* [NOD-1561] Add TestValidateMedianTime

* [NOD-1561] Remove redundant variable
2020-11-23 17:18:30 +02:00
Ori Newman
bb2d7f72ac [NOD-1560] Add TestValidateTransactionInIsolation (#1140)
* [NOD-1560] Add TestValidateTransactionInIsolation

* [NOD-1560] Make ForAllNets copy the params before mutating them

* [NOD-1560] Remove redundant continue

* [NOD-1560] Don't change finality duration
2020-11-23 16:28:59 +02:00
Ori Newman
c1505b4748 [NOD-1555] Use stageDiff to update virtualDiffParents (#1139)
* [NOD-1555] Filter ancestors in updateVirtualDiffParents

* [NOD-1555] Use stageDiff to update virtualDiffParents

* [NOD-1555] Don't add existing blocks in addToVirtualDiffParents

* [NOD-1555] Remove redundant check

* [NOD-1555] Fix log and rename removeAncestorsFromVirtualDiffParents->removeAncestorsFromVirtualDiffParentsAndAssignDiffChild

* [NOD-1555] Add logs

* [NOD-1555] Fix comment

* [NOD-1555] Fix logs
2020-11-23 15:09:39 +02:00
stasatdaglabs
dec9ef5f75 [NOD-1555] Implement TestResolveBlockStatusSanity (#1138)
* [NOD-1555] Implement TestResolveBlockStatusSanity.

* [NOD-1555] Fix the test name string.
2020-11-23 14:29:41 +02:00
Mike Zak
5211727206 [NOD-1557] Cover the consensusStateManager package in trace logs (#1135)
* [NOD-1557] Add trace logs in add_block_to_virtual.go.

* [NOD-1557] Add trace logs in resolve_block_status.go.

* [NOD-1557] Add trace logs in calculate_past_utxo.go.

* [NOD-1557] Add trace logs in finality.go.

* [NOD-1557] Add trace logs in multisets.go.

* [NOD-1557] Fix compilation errors.

* [NOD-1557] Add trace logs to verify_and_build_utxo.go.

* [NOD-1557] Add trace logs to update_virtual.go.

* [NOD-1557] Add trace logs to set_pruning_utxo_set.go.

* [NOD-1557] Add trace logs to populate_tx_with_utxo_entries.go.

* [NOD-1557] Add trace logs to pick_virtual_parents.go.

* [NOD-1557] Make go vet happy.

* [NOD-1557] Clarify that some logic in AddBlockToVirtual is there for the sake of logging alone.

* [NOD-1557] Call blockStatusStore directly in AddBlockToVirtual when refetching the block status.
2020-11-23 13:08:10 +02:00
Elichai Turkel
fafe1d534f Add TestSequenceLocksActive back (#1133) 2020-11-22 17:17:39 +02:00
Elichai Turkel
c56a5336f3 Re-add TestPruningDepth (#1132) 2020-11-22 17:04:13 +02:00
Elichai Turkel
b3a3121725 Add TestFinality back (#1129)
* Add VirtualFinalityPoint to TestConsensusStateManager

* Add TestFinality back
2020-11-22 12:30:27 +02:00
Svarog
950dd0cc8d [NOD-1556] Add some logs (#1110)
* [NOD-1556] Add logs regarding block status and virtual blue score

* [NOD-1556] UTXODiffAlgebra: add the offending outpoint to the text of errors

* [NOD-1556] Make checkIntersectionWithRule return ok as well
2020-11-19 11:17:05 +02:00
stasatdaglabs
bb244706ea [NOD-1543] Optimize the performance of Count() in BlockHeaderStore and BlockStore (#1109)
* [NOD-1543] Optimize Count() in BlockHeaderStore.

* [NOD-1543] Optimize Count() in BlockStore.

* [NOD-1543] Fix commitCount.

* [NOD-1543] Explicitly initialize count to 0.
2020-11-18 16:35:32 +02:00
stasatdaglabs
ed386bbc8f [NOD-1550] Don't request blocks for invs that are known to be orphans (#1108)
* [NOD-1550] Implement IsOrphan().

* [NOD-1550] Don't request blocks for invs that are known to be orphans.
2020-11-18 13:37:14 +02:00
stasatdaglabs
75d21d39cc [NOD-1549] Properly handle errors in unorphanBlock (#1107)
* [NOD-1549] In AddBlock, simply log RuleErrors.

* [NOD-1549] Properly handle errors in unorphanBlock.
2020-11-18 12:19:30 +02:00
Elichai Turkel
3f92ddd827 Add blueScore to RPC GetBlock, and add more INFO logs (#1103)
* Add BlueScore to RPC command GetBlock

* Add info logs when getting new blocks from the p2p
2020-11-18 11:19:12 +02:00
Ori Newman
8500acd86b [NOD-1548] Remove PoW check from tests (#1105)
* [NOD-1548] Add TestDifficulty and remove PoW check from tests

* [NOD-1548] Add TestSkipProofOfWork

* [NOD-1548] Remove TestDifficulty
2020-11-18 10:27:29 +02:00
Elichai Turkel
5b037950d8 Fix double printing the mainnet has not launched yet message (#1101) 2020-11-18 09:16:58 +02:00
stasatdaglabs
184911f76e [NOD-1547] Make the SendAddresses flow not one-time for the sake of DNSSeeder (#1104)
* [NOD-1547] Make the SendAddresses flow not one-time for the sake of DNSSeeder.

* [NOD-1547] Add all special commands to chooseRouteForCommand.
2020-11-17 18:05:14 +02:00
stasatdaglabs
7479f5f5e8 [NOD-1545] Fix incorrect block difficulty calculation in buildHeader. (#1102) 2020-11-17 16:40:55 +02:00
Ori Newman
891095563e Fix blocks order (#1099) 2020-11-17 16:00:16 +02:00
Ori Newman
60c24d8dea Fix TestBlueBlockWindow (#1098)
* Fix TestBlueBlockWindow

* Add comments
2020-11-17 16:00:16 +02:00
Svarog
d4993c1d06 [NOD-1542] Don't try to return more addresses then we have (#1097)
* [NOD-1542] Don't try to return more addresses then we have

* [NOD-1542] Allocate according to updated count
2020-11-17 16:00:16 +02:00
Elichai Turkel
c785ca0e52 Add more compactBits tests (#1096) 2020-11-17 16:00:16 +02:00
Svarog
9eb5c4a0ed [NOD-1532] Make all nets equal in mining difficulty (#1095)
* [NOD-1532] Make all nets equal in mining difficulty

* [NOD-1532] Fix comments
2020-11-17 16:00:16 +02:00
stasatdaglabs
9d5d1b02dc [NOD-1538] Implement a simple orphan pool (#1093)
* [NOD-1538] Implement a simple orphan pool.

* [NOD-1538] Connect the orphan pool to the appropriate flows.

* [NOD-1538] Make UnorphanBlocks actually unorphan blocks.

* [NOD-1538] Fix logs.

* [NOD-1538] Make unorphaned blocks call LogBlock.

* [NOD-1538] Fix a log and some bad names.

* [NOD-1538] Don't return an error from LogBlock.

* [NOD-1538] Pass a pointer to hash in findChildOrphansOfBlock.

* [NOD-1538] Extract addChildOrphansToProcessQueue to a separate function.
2020-11-17 16:00:16 +02:00
stasatdaglabs
213be67c47 [NOD-1538] Fix isBlockInHeaderPruningPointFuture. (#1094) 2020-11-17 16:00:16 +02:00
Ori Newman
14d7ab5fc6 Add TestBigToCompact and TestCompactToBig (#1092)
* Add TestBigToCompact and TestCompactToBig

* Add tests
2020-11-17 16:00:16 +02:00
Mike Zak
7224d58940 [NOD-1532] Add comments 2020-11-17 16:00:16 +02:00
Mike Zak
b2188f5993 [NOD-1532] AlwaysCallResolveBlockStatus in BuildBlock + Fixes to make it work if there's nothing to resolve 2020-11-17 16:00:16 +02:00
Mike Zak
dbd15aecf5 [NOD-1532] Invert condition in isViolatingFinality 2020-11-17 16:00:16 +02:00
Mike Zak
66f5a5bd7d [NOD-1532] Genesis is not violating finality by definition 2020-11-17 16:00:16 +02:00
Mike Zak
c994200878 [NOD-1532] ResolveBlockStatus should return the blockStatus 2020-11-17 16:00:16 +02:00
Ori Newman
7050ebeac9 [NOD-1541] Add TestPastMedianTime (#1091) 2020-11-17 16:00:16 +02:00
Svarog
f2df48139f [NOD-1532] Remove virtual from ParentChildren when checking for virtualSelectedParent candidates (#1089)
* [NOD-1532] Remove virtual from ParentChildren when checking for virtualSelectedParent candidates

* [NOD-1532] Fix minor issues
2020-11-17 16:00:16 +02:00
stasatdaglabs
48d8137604 [NOD-1538] Implement GetBlockCount. 2020-11-17 16:00:16 +02:00
stasatdaglabs
310cf0bb9b [NOD-1538] Remove bad check in selectPeerForIBD. 2020-11-17 16:00:16 +02:00
Ori Newman
b6c47fdd21 [NOD-1535] fix reachability tests (#1087)
* [NOD-1535] Don't use pointer to outpoint when serializing

* [NOD-1535] Fix reachability tests
2020-11-17 16:00:16 +02:00
Ori Newman
e6a2b7366f [NOD-1535] Don't use pointer to outpoint when serializing (#1086) 2020-11-17 16:00:16 +02:00
Mike Zak
d8f72e2b27 [NOD-1532] Update VirtualUTXODiffParents diffs even if list didn't change 2020-11-17 16:00:16 +02:00
stasatdaglabs
08749deaeb [NOD-1538] Fix mempool not wrapping consensus errors and bad invalid message handling (#1082)
* [NOD-1538] Correct messages.proto.

* [NOD-1538] Fix invalid message handling.

* [NOD-1538] Fix mempool not wrapping consensus errors.

* [NOD-1538] Extract wrapping logic to a separate function.

* [NOD-1538] Extract wrapping logic to an even better separate function.
2020-11-17 16:00:16 +02:00
Mike Zak
83a88d9989 [NOD-1532] newUTXOSetIterator should start with -1 index 2020-11-17 16:00:16 +02:00
Mike Zak
151910c27a [NOD-1532] Check UTXOCommitment for all blocks 2020-11-17 16:00:16 +02:00
Mike Zak
fca8ed57bd [NOD-1532] Add another block in TestUTXOCommitment 2020-11-17 16:00:16 +02:00
Ori Newman
56679818be [NOD-1535] Add non coinbase transactions to diff (#1084) 2020-11-17 16:00:16 +02:00
Mike Zak
f07f2edad2 [NOD-1532] Properly deal with selectedParentStatuses in buildBlockWithParents 2020-11-17 16:00:16 +02:00
Mike Zak
2dcfe90850 [NOD-1532] Shouldn't update parent diff if the parent is not UTXO-verified 2020-11-17 16:00:16 +02:00
Mike Zak
dc80a39c54 [NOD-1532] OpTrueScript should also return the redeem script 2020-11-17 16:00:16 +02:00
Mike Zak
34be898491 [NOD-1532] utxoSetIterator should be a pointer receiver 2020-11-17 16:00:16 +02:00
Mike Zak
f4a2fbf64f [NOD-1532] Fixes in updateVirtualDiffParents 2020-11-17 16:00:16 +02:00
Ori Newman
a0c6076ccc [NOD-1535] Add new block to virtual diff parents only if it's valid (#1077) 2020-11-17 16:00:16 +02:00
Mike Zak
fddce00d08 [NOD-1532] Fixes in updateVirtualDiffParent 2020-11-17 16:00:16 +02:00
Mike Zak
ae682d59f7 [NOD-1532] Fixes in updateVirtualDiffParent 2020-11-17 16:00:16 +02:00
Ori Newman
347f3de15c [NOD-1535] fix reachability test (#1075)
* [NOD-1535] Don't compare pointers

* [NOD-1535] Fix condition on updateVirtualDiffParents
2020-11-17 16:00:16 +02:00
stasatdaglabs
a34091991a [NOD-1538] Fix MinimalNetAdapter and don't insert BlockRelations before making sure the block's parents exist (#1074)
* [NOD-1538] Fix minimal net adapter.

* [NOD-1538] Don't insert block relation until we've validated that the block's parents exist.

* [NOD-1538] Don't hold addressManager in MinimalNetAdapter.

* [NOD-1538] Fix a comment in messages.proto.
2020-11-17 16:00:16 +02:00
Mike Zak
efe1986a56 [NOD-1532] Don't validate coinbase transaction in normal flow 2020-11-17 16:00:16 +02:00
Mike Zak
3ab507b66f [NOD-1532] Use correct coinbase transaction in buildBlockWith Parents 2020-11-17 16:00:16 +02:00
Ori Newman
afbad73c0b [NOD-1535] Don't compare pointers (#1072) 2020-11-17 16:00:16 +02:00
Mike Zak
a1fa17d872 [NOD-1532] Add DiscardAllStores to TestConsensus 2020-11-17 16:00:16 +02:00
Ori Newman
b50421beee [NOD-1535] Don't reuse pointers on loop (#1069)
* [NOD-1535] Don't reuse pointers on loop

* [NOD-1535] Don't reuse pointers on loop
2020-11-17 16:00:16 +02:00
Mike Zak
aeded07815 [NOD-1532] Make BuildBlockWithParents resolve the status of the new block's selectedParent 2020-11-17 16:00:16 +02:00
Mike Zak
7d14f24b84 [NOD-1532] Fix some error messages 2020-11-17 16:00:16 +02:00
Mike Zak
c52b8100c6 [NOD-1532] make dagtopologymanager test external 2020-11-17 16:00:16 +02:00
Mike Zak
f52cddc25c [NOD-1532] Remove consensus rule that requires blocks are sorted by hash 2020-11-17 16:00:16 +02:00
Ori Newman
fc5e39f6cc [NOD-1535] fix reachability test (#1061)
* Revert "[NOD-1500] Delete integration tests"

This reverts commit fcb57a2066.

* [NOD-1518] hashserialization -> consenusserialization

* [NOD-1518] Fix add genesis to virtual

* [NOD-1518] Fix a bug in SerializeCoinbasePayload.

* [NOD-1518] Fix a loop error and make pastMedianTime behave correctly everywhere on genesis.

* [NOD-1518] Fix another bug and an infinite loop.

* [NOD-1518] Fix uninitialized slice.

* [NOD-1518] Fix bad should-commit checks and another infinite loop.

* [NOD-1518] Fix nil serialization.

* [NOD-1518] Rename blockHash to currentBlockHash.

* [NOD-1518] Move the check whether stagedVirtualUTXOSet != nil to the top of commitVirtualUTXODiff.

* [NOD-1518] Simplify utxoDiffStore.Commit.

* [NOD-1518] Unextract resolveBlockStatusAndCheckFinality.

* [NOD-1518] Move no-transactions logic into CalculateIDMerkleRoot.

* [NOD-1518] Remove redundant is-staged check.

* [NOD-1518] Fix merge errors.

* [NOD-1518] Don't write anything if utxoDiffChild is nil.

* [NOD-1518] Stage virtualAcceptanceData and virtualMultiset.

* [NOD-1518] Fix bugs in getBlockTemplate and submitBlock.

* [NOD-1518] Fix bad validation order in validateHeaderInContext.

* [NOD-1518] Fix bug in Next().

* [NOD-1518] Fix nil dereference of subnetworks in AddressCache.

* [NOD-1518] Fix multisetStore.Get returning a pointer to a multiset that is changed in place.

* [NOD-1518] Break on genesis in countSubtrees.

* [NOD-1518] Fix createBlockLocator.

* [NOD-1518] Fix MsgTxToDomainTransaction.

* [NOD-1518] Set MaxTxVersion to 1.

* [NOD-1518] Fix missing error handling, bug in MsgTxToDomainTransaction, and bad subnetwork equality check.

* [NOD-1518] Fix bug in hasUTXOByOutpointFromStagedVirtualUTXODiff.

* [NOD-1518] Remove irrelevant comments.

* [NOD-1518] Generate transactions with sufficient fee in tx_relay_test.

* [NOD-1518] Fix broken RPC handlers.

* [NOD-1518] Fix merge errors.

* [NOD-1518] Fix bad exists check in restorePastUTXO and missing genesis check in CalculatePastUTXOAndAcceptanceData.

* [NOD-1518] Add a comment.

* [NOD-1518] Use a regular mutex instead of a read-write mutex in consensus to avoid dealing with sneaky not-actually-read functions.

* [NOD-1518] Fix a deadlock in GetVirtualSelectedParent.

* [NOD-1518] Fix missing handler registration for CmdHeader.

* [NOD-1518] Fix processHeader calling OnNewBlock and LogBlock. Also fix conversion errors in IBDRootUTXOSetAndBlock.

* [NOD-1518] Fix bad Command() in MsgIBDRootUTXOSetAndBlock.

* [NOD-1518] Fix bad SyncStateMissingUTXOSet logic in resolveSyncState.

* [NOD-1518] Rename mode to syncState.

* [NOD-1518] Fix headers-only blocks coming in after the consensus thinks it's synced.

* [NOD-1518] Fix selectedChildIterator.Next not ignoring virtual, infinite loop in HashSet.Length().

* [NOD-1518] Fix not-properly wrapped IBD blocks.

* [NOD-1518] Fix bad conversion in RequestIBDBlocks.

* [NOD-1518] Fix bad string for CmdRequestHeaders.

* [NOD-1518] Fix bad string for CmdDoneHeaders.

* [NOD-1518] Fix bad Command() for MsgIBDRootNotFound.

* [NOD-1518] Fix bad areHeaderTipsSyncedMaxTimeDifference value.

* [NOD-1518] Add missing string for CmdRequestIBDBlocks.

* [NOD-1518] Fix bad check for SyncStateMissingBlockBodies.

* [NOD-1518] Fix bad timeout durations in tests.

* [NOD-1518] Fix IBD blocks not calling OnNewBlock.

* [NOD-1518] Change when IBD finishes.

* [NOD-1518] Properly clone utxoDiffChild.

* [NOD-1535] Fix reachability tests

* [NOD-1518] Fix merge errors.

* [NOD-1518] Move call to LogBlock to into OnNewBlock.

* [NOD-1518] Return "not implemented" in unimplemented RPC handlers.

* [NOD-1518] Extract cloning of hashes to a method over DomainHash.

* [NOD-1518] Use isHeaderOnlyBlock.

* [NOD-1518] Use constants.TransactionVersion.

* [NOD-1518] Break immediately if we reached the virtual in SelectedChildIterator.

* [NOD-1518] Don't stage nil utxoDiffChild.

* [NOD-1518] Properly check the genesis hash in CalculatePastUTXOAndAcceptanceData.

* [NOD-1518] Explain why we break on current == nil in countSubtrees.

* [NOD-1518] Add a comment explaining why we check against StatusValid in resolveSyncState.

* [NOD-1535] Add external reachability tests

* [NOD-1535] Fix reachability tests and fix related bugs

* [NOD-1535] Add setters fox reindex slack and window

* [NOD-1535] Remove redundant line

* [NOD-1535] Add comment

* [NOD-1535] Fix comments

* [NOD-1535] Rename DBReader->DatabaseContext

* [NOD-1535] Check that reindex root is changed

* [NOD-1535] Fix calculateNewTips

Co-authored-by: Mike Zak <feanorr@gmail.com>
Co-authored-by: stasatdaglabs <stas@daglabs.com>
2020-11-17 16:00:16 +02:00
Svarog
8ccf381fc7 [NOD-1532] csm unit tests (#1059)
* Revert "[NOD-1500] Delete integration tests"

This reverts commit fcb57a2066.

* [NOD-1518] hashserialization -> consenusserialization

* [NOD-1518] Fix add genesis to virtual

* [NOD-1518] Fix a bug in SerializeCoinbasePayload.

* [NOD-1518] Fix a loop error and make pastMedianTime behave correctly everywhere on genesis.

* [NOD-1518] Fix another bug and an infinite loop.

* [NOD-1518] Fix uninitialized slice.

* [NOD-1518] Fix bad should-commit checks and another infinite loop.

* [NOD-1518] Fix nil serialization.

* [NOD-1518] Rename blockHash to currentBlockHash.

* [NOD-1518] Move the check whether stagedVirtualUTXOSet != nil to the top of commitVirtualUTXODiff.

* [NOD-1518] Simplify utxoDiffStore.Commit.

* [NOD-1518] Unextract resolveBlockStatusAndCheckFinality.

* [NOD-1518] Move no-transactions logic into CalculateIDMerkleRoot.

* [NOD-1518] Remove redundant is-staged check.

* [NOD-1518] Fix merge errors.

* [NOD-1518] Don't write anything if utxoDiffChild is nil.

* [NOD-1518] Stage virtualAcceptanceData and virtualMultiset.

* [NOD-1518] Fix bugs in getBlockTemplate and submitBlock.

* [NOD-1518] Fix bad validation order in validateHeaderInContext.

* [NOD-1518] Fix bug in Next().

* [NOD-1518] Fix nil dereference of subnetworks in AddressCache.

* [NOD-1518] Fix multisetStore.Get returning a pointer to a multiset that is changed in place.

* [NOD-1518] Break on genesis in countSubtrees.

* [NOD-1518] Fix createBlockLocator.

* [NOD-1518] Fix MsgTxToDomainTransaction.

* [NOD-1518] Set MaxTxVersion to 1.

* [NOD-1518] Fix missing error handling, bug in MsgTxToDomainTransaction, and bad subnetwork equality check.

* [NOD-1518] Fix bug in hasUTXOByOutpointFromStagedVirtualUTXODiff.

* [NOD-1518] Remove irrelevant comments.

* [NOD-1518] Generate transactions with sufficient fee in tx_relay_test.

* [NOD-1518] Fix broken RPC handlers.

* [NOD-1518] Fix merge errors.

* [NOD-1518] Fix bad exists check in restorePastUTXO and missing genesis check in CalculatePastUTXOAndAcceptanceData.

* [NOD-1518] Add a comment.

* [NOD-1518] Use a regular mutex instead of a read-write mutex in consensus to avoid dealing with sneaky not-actually-read functions.

* [NOD-1518] Fix a deadlock in GetVirtualSelectedParent.

* [NOD-1518] Fix missing handler registration for CmdHeader.

* [NOD-1518] Fix processHeader calling OnNewBlock and LogBlock. Also fix conversion errors in IBDRootUTXOSetAndBlock.

* [NOD-1518] Fix bad Command() in MsgIBDRootUTXOSetAndBlock.

* [NOD-1518] Fix bad SyncStateMissingUTXOSet logic in resolveSyncState.

* [NOD-1518] Rename mode to syncState.

* [NOD-1518] Fix headers-only blocks coming in after the consensus thinks it's synced.

* [NOD-1518] Fix selectedChildIterator.Next not ignoring virtual, infinite loop in HashSet.Length().

* [NOD-1518] Fix not-properly wrapped IBD blocks.

* [NOD-1532] Add TestMultiset

* [NOD-1518] Fix bad conversion in RequestIBDBlocks.

* [NOD-1518] Fix bad string for CmdRequestHeaders.

* [NOD-1518] Fix bad string for CmdDoneHeaders.

* [NOD-1518] Fix bad Command() for MsgIBDRootNotFound.

* [NOD-1532] Add TestPastUTXOMultiset

* [NOD-1518] Fix bad areHeaderTipsSyncedMaxTimeDifference value.

* [NOD-1532] Added TestDoubleSpends

* [NOD-1518] Add missing string for CmdRequestIBDBlocks.

* [NOD-1518] Fix bad check for SyncStateMissingBlockBodies.

* [NOD-1518] Fix bad timeout durations in tests.

* [NOD-1518] Fix IBD blocks not calling OnNewBlock.

* [NOD-1518] Change when IBD finishes.

* [NOD-1518] Properly clone utxoDiffChild.

* [NOD-1532] Update hashes of blocks

* [NOD-1532] Fix genesis blocks and a few more bugs

* [NOD-1532] Bugfix: incorrect key passed to dbTx.Put

* [NOD-1532] Make sure there's no nil payloads

* [NOD-1532] Fix AddBlockToVirtual

* [NOD-1532] Update tips and virtualDiffParents properly

* [NOD-1532] Allow nil payload

* [NOD-1532] Check for actual error and not just some RuleError

* [NOD-1532] Get rid of SimpleCoinbaseData and make OpTrueScript P2SH

* [NOD-1532] If coinbaseData is nil - fill in with generic coinbaseData

Co-authored-by: Ori Newman <orinewman1@gmail.com>
Co-authored-by: stasatdaglabs <stas@daglabs.com>
2020-11-17 16:00:16 +02:00
stasatdaglabs
f320887bff [NOD-1538] Fix bad allocation in notBannedAddressesWithException. 2020-11-17 16:00:16 +02:00
stasatdaglabs
eef5e3768c [NOD-1518] Fix genesis block insertion and integration tests (#1013)
* Revert "[NOD-1500] Delete integration tests"

This reverts commit fcb57a2066.

* [NOD-1518] hashserialization -> consenusserialization

* [NOD-1518] Fix add genesis to virtual

* [NOD-1518] Fix a bug in SerializeCoinbasePayload.

* [NOD-1518] Fix a loop error and make pastMedianTime behave correctly everywhere on genesis.

* [NOD-1518] Fix another bug and an infinite loop.

* [NOD-1518] Fix uninitialized slice.

* [NOD-1518] Fix bad should-commit checks and another infinite loop.

* [NOD-1518] Fix nil serialization.

* [NOD-1518] Rename blockHash to currentBlockHash.

* [NOD-1518] Move the check whether stagedVirtualUTXOSet != nil to the top of commitVirtualUTXODiff.

* [NOD-1518] Simplify utxoDiffStore.Commit.

* [NOD-1518] Unextract resolveBlockStatusAndCheckFinality.

* [NOD-1518] Move no-transactions logic into CalculateIDMerkleRoot.

* [NOD-1518] Remove redundant is-staged check.

* [NOD-1518] Fix merge errors.

* [NOD-1518] Don't write anything if utxoDiffChild is nil.

* [NOD-1518] Stage virtualAcceptanceData and virtualMultiset.

* [NOD-1518] Fix bugs in getBlockTemplate and submitBlock.

* [NOD-1518] Fix bad validation order in validateHeaderInContext.

* [NOD-1518] Fix bug in Next().

* [NOD-1518] Fix nil dereference of subnetworks in AddressCache.

* [NOD-1518] Fix multisetStore.Get returning a pointer to a multiset that is changed in place.

* [NOD-1518] Break on genesis in countSubtrees.

* [NOD-1518] Fix createBlockLocator.

* [NOD-1518] Fix MsgTxToDomainTransaction.

* [NOD-1518] Set MaxTxVersion to 1.

* [NOD-1518] Fix missing error handling, bug in MsgTxToDomainTransaction, and bad subnetwork equality check.

* [NOD-1518] Fix bug in hasUTXOByOutpointFromStagedVirtualUTXODiff.

* [NOD-1518] Remove irrelevant comments.

* [NOD-1518] Generate transactions with sufficient fee in tx_relay_test.

* [NOD-1518] Fix broken RPC handlers.

* [NOD-1518] Fix merge errors.

* [NOD-1518] Fix bad exists check in restorePastUTXO and missing genesis check in CalculatePastUTXOAndAcceptanceData.

* [NOD-1518] Add a comment.

* [NOD-1518] Use a regular mutex instead of a read-write mutex in consensus to avoid dealing with sneaky not-actually-read functions.

* [NOD-1518] Fix a deadlock in GetVirtualSelectedParent.

* [NOD-1518] Fix missing handler registration for CmdHeader.

* [NOD-1518] Fix processHeader calling OnNewBlock and LogBlock. Also fix conversion errors in IBDRootUTXOSetAndBlock.

* [NOD-1518] Fix bad Command() in MsgIBDRootUTXOSetAndBlock.

* [NOD-1518] Fix bad SyncStateMissingUTXOSet logic in resolveSyncState.

* [NOD-1518] Rename mode to syncState.

* [NOD-1518] Fix headers-only blocks coming in after the consensus thinks it's synced.

* [NOD-1518] Fix selectedChildIterator.Next not ignoring virtual, infinite loop in HashSet.Length().

* [NOD-1518] Fix not-properly wrapped IBD blocks.

* [NOD-1518] Fix bad conversion in RequestIBDBlocks.

* [NOD-1518] Fix bad string for CmdRequestHeaders.

* [NOD-1518] Fix bad string for CmdDoneHeaders.

* [NOD-1518] Fix bad Command() for MsgIBDRootNotFound.

* [NOD-1518] Fix bad areHeaderTipsSyncedMaxTimeDifference value.

* [NOD-1518] Add missing string for CmdRequestIBDBlocks.

* [NOD-1518] Fix bad check for SyncStateMissingBlockBodies.

* [NOD-1518] Fix bad timeout durations in tests.

* [NOD-1518] Fix IBD blocks not calling OnNewBlock.

* [NOD-1518] Change when IBD finishes.

* [NOD-1518] Properly clone utxoDiffChild.

* [NOD-1518] Fix merge errors.

* [NOD-1518] Move call to LogBlock to into OnNewBlock.

* [NOD-1518] Return "not implemented" in unimplemented RPC handlers.

* [NOD-1518] Extract cloning of hashes to a method over DomainHash.

* [NOD-1518] Use isHeaderOnlyBlock.

* [NOD-1518] Use constants.TransactionVersion.

* [NOD-1518] Break immediately if we reached the virtual in SelectedChildIterator.

* [NOD-1518] Don't stage nil utxoDiffChild.

* [NOD-1518] Properly check the genesis hash in CalculatePastUTXOAndAcceptanceData.

* [NOD-1518] Explain why we break on current == nil in countSubtrees.

* [NOD-1518] Add a comment explaining why we check against StatusValid in resolveSyncState.

Co-authored-by: Mike Zak <feanorr@gmail.com>
Co-authored-by: Ori Newman <orinewman1@gmail.com>
2020-11-12 15:19:39 +02:00
Ori Newman
7a7821e1c8 [NOD-1313] Refactor AddressManager (#918) (#1049)
* [NOD-1313] Refactor AddressManager (#918)

* [NOD-1313] Refactor AddressManager.

* [NOD-1313]Remove old tests.Minor improvements,fixes.

* [NOD-1313] After merge fixes. Fix import cycle.

* [NOD-1313] Integration tests fixes.

* [NOD-1313] Allocate new slice for the returned key.

* [NOD-1313] AddressManager improvements and fixes.

* Move local and banned addresses to separate lists.
* Move AddressManager config to the separate file.
* Add LocalAddressManager.
* Remove redundant KnownAddress structure.
* Restore local addresses functionality.
* Call initListeners from the LocalAddressManager.
* AddressManager minor improvements and fixes.

* [NOD-1313] Minor fixes.

* [NOD-1313] Implement HandleGetPeerAddresses. Refactoring.

* [NOD-1313] After-merge fixes.

* [NOD-1313] Minor improvements.

* AddressManager: added BannedAddresses() method.
* AddressManager: HandleGetPeerAddresses() add banned addresses
  separately.
* AddressManager: remove addressEntry redundant struct.
* ConnectionManager: checkOutgoingConnections() minor improvements and
  fixes.
* Minor refactoring.
* Minor fixes.

* [NOD-1313] GetPeerAddresses RPC message update

* GetPeerAddresses RPC: add BannedAddresses in the separate field.
* Update protobuf.

* [NOD-1534] Update messages.pb.go

Co-authored-by: Kirill <gammerxpower@gmail.com>
2020-11-12 14:40:41 +02:00
Svarog
37fbdcb453 [NOD-1526] Restore txscript tests (#1019)
* [NOD-1526] Fix compilation errors

* [NOD-1526] Make MsgTx.PayloadHash non-pointer

* [NOD-1526] Fixed many tests

* [NOD-1526] Fix reference_test.go

* [NOD-1526] Removed last instances of appmessage in consensus

* [NOD-1526] No need to check for subnetwork
2020-11-12 10:22:17 +02:00
Svarog
135ffbd4f2 [NOD-1529] Add getters + AddBlock to TestConsensus (#1025)
* [NOD-1529] Add all stores and processes to consensus, and add access to TestConsensus

* [NOD-1529] Move the getters of TestConsensus to separate file

* [NOD-1529] Add AddBlock to TestConsensus

* [NOD-1529] Update NewTestConsensus to be more all-encompassing

* [NOD-1529] Remove test directory in teardown

* [NOD-1529] Add ForAllNets function

* [NOD-1529] Add comment
2020-11-11 12:31:13 +02:00
Ori Newman
4736213ba4 [NOD-1528] Make data stores copy data on stage (#1020)
* [NOD-1528] Make data stores copy data on stage

* [NOD-1528] Add proto objects to serialize consensus state objects

* [NOD-1528] Fix receiver names

* [NOD-1528] Add copy to block store and utxo diff staging

* [NOD-1528] Return errors where needed
2020-11-10 18:32:42 +02:00
oudeis
8290fadd3a [NOD-1521] use staticcheck (#1015)
* [NOD-1521] Use static check as part of jenkins to check for swallowed errors

* [NOD-1521] added staticcheck installation

* [NOD-1521] Fix static check errors

Co-authored-by: Ori Newman <orinewman1@gmail.com>
2020-11-10 17:59:35 +02:00
Ori Newman
23c1ea6c31 [NOD-1525] Implement headers first ibd (#1017)
* [NOD-1525] Implement headers first IBD

* [NOD-1525] Fix proto translators

* [NOD-1525] Register missing flows

* [NOD-1525] Rename SyncStateNormal->SyncStateRelay, simplifiy IBD peer selection code and get rid of panic in FinishIBD

* [NOD-1525] Remove redundant methods from interface
2020-11-10 16:14:51 +02:00
Ori Newman
31c5264430 [NOD-1527] Allow to process headers while in missing utxo set sync state (#1018)
* [NOD-1527] Allow to process headers while in missing utxo set sync state

* [NOD-1527] Add isHeaderOnlyBlock function
2020-11-10 14:43:18 +02:00
Ori Newman
32da4440ba [NOD-1495] Disallow non native transactions (#988)
* [NOD-1495] Disallow non native transactions

* [NOD-1495] Use deserializeUTXOSetBytes

* [NOD-1495] Delete checkNoNonNativeTransactions

* [NOD-1495] Invert condition in checkTransactionPayload

Co-authored-by: Mike Zak <feanorr@gmail.com>
2020-11-09 17:15:16 +02:00
Svarog
e7a61c7edf [NOD-1524] Add lock to consensus (#1014) 2020-11-09 16:21:09 +02:00
Svarog
6db337c8c5 [NOD-1519] Add TestAPI for BuildBlockWithParents (#1011)
* [NOD-1519] Separate BlockBuilder and BlockProcessor

* [NOD-1519] Wire blockBuilder properly + implement buildBlockWithParents

* [NOD-1519] Added testapi package, TestConsensus interface, and TestConsensus factory

* [NOD-1519] Add comments

* [NOD-1519] Separate TestBlockBuilder out of BlockBuilder

* [NOD-1519] TestBlockBuilder should also implement BlockBuilder

* [NOD-1519] Add NewTestConsensus to factory interface
2020-11-09 12:02:42 +02:00
Svarog
2282e36196 [NOD-1522] Add IsEqual to SubnetworkID (#1012)
* [NOD-1519] Add IsEqual to SubnetworkID

* [NOD-1522] Added comment
2020-11-09 10:52:57 +02:00
oudeis
72b5832f30 Update to version 0.8.0 2020-11-09 07:07:30 +00:00
270 changed files with 19078 additions and 5824 deletions

View File

@@ -46,7 +46,7 @@ func StartApp() error {
// initializes logging and configures it accordingly.
cfg, err := config.LoadConfig()
if err != nil {
fmt.Fprint(os.Stderr, err)
fmt.Fprintln(os.Stderr, err)
return err
}
defer panics.HandlePanic(log, "MAIN", nil)

View File

@@ -17,9 +17,9 @@ func DomainBlockToMsgBlock(domainBlock *externalapi.DomainBlock) *MsgBlock {
}
}
// DomainBlockHeaderToBlockHeader converts an externalapi.DomainBlockHeader to BlockHeader
func DomainBlockHeaderToBlockHeader(domainBlockHeader *externalapi.DomainBlockHeader) *BlockHeader {
return &BlockHeader{
// DomainBlockHeaderToBlockHeader converts an externalapi.DomainBlockHeader to MsgBlockHeader
func DomainBlockHeaderToBlockHeader(domainBlockHeader *externalapi.DomainBlockHeader) *MsgBlockHeader {
return &MsgBlockHeader{
Version: domainBlockHeader.Version,
ParentHashes: domainBlockHeader.ParentHashes,
HashMerkleRoot: &domainBlockHeader.HashMerkleRoot,
@@ -44,8 +44,8 @@ func MsgBlockToDomainBlock(msgBlock *MsgBlock) *externalapi.DomainBlock {
}
}
// BlockHeaderToDomainBlockHeader converts a BlockHeader to externalapi.DomainBlockHeader
func BlockHeaderToDomainBlockHeader(blockHeader *BlockHeader) *externalapi.DomainBlockHeader {
// BlockHeaderToDomainBlockHeader converts a MsgBlockHeader to externalapi.DomainBlockHeader
func BlockHeaderToDomainBlockHeader(blockHeader *MsgBlockHeader) *externalapi.DomainBlockHeader {
return &externalapi.DomainBlockHeader{
Version: blockHeader.Version,
ParentHashes: blockHeader.ParentHashes,
@@ -77,7 +77,7 @@ func DomainTransactionToMsgTx(domainTransaction *externalapi.DomainTransaction)
LockTime: domainTransaction.LockTime,
SubnetworkID: domainTransaction.SubnetworkID,
Gas: domainTransaction.Gas,
PayloadHash: &domainTransaction.PayloadHash,
PayloadHash: domainTransaction.PayloadHash,
Payload: domainTransaction.Payload,
}
}
@@ -114,6 +114,12 @@ func MsgTxToDomainTransaction(msgTx *MsgTx) *externalapi.DomainTransaction {
for _, txOut := range msgTx.TxOut {
transactionOutputs = append(transactionOutputs, txOutToDomainTransactionOutput(txOut))
}
payload := make([]byte, 0)
if msgTx.Payload != nil {
payload = msgTx.Payload
}
return &externalapi.DomainTransaction{
Version: msgTx.Version,
Inputs: transactionInputs,
@@ -121,8 +127,8 @@ func MsgTxToDomainTransaction(msgTx *MsgTx) *externalapi.DomainTransaction {
LockTime: msgTx.LockTime,
SubnetworkID: msgTx.SubnetworkID,
Gas: msgTx.Gas,
PayloadHash: *msgTx.PayloadHash,
Payload: msgTx.Payload,
PayloadHash: msgTx.PayloadHash,
Payload: payload,
}
}

View File

@@ -34,7 +34,7 @@ const (
CmdVerAck
CmdRequestAddresses
CmdAddresses
CmdRequestIBDBlocks
CmdRequestHeaders
CmdBlock
CmdTx
CmdPing
@@ -48,10 +48,15 @@ const (
CmdInvTransaction
CmdRequestTransactions
CmdIBDBlock
CmdRequestNextIBDBlocks
CmdDoneIBDBlocks
CmdDoneHeaders
CmdTransactionNotFound
CmdReject
CmdHeader
CmdRequestNextHeaders
CmdRequestIBDRootUTXOSetAndBlock
CmdIBDRootUTXOSetAndBlock
CmdRequestIBDBlocks
CmdIBDRootNotFound
// rpc
CmdGetCurrentNetworkRequestMessage
@@ -107,28 +112,33 @@ const (
// ProtocolMessageCommandToString maps all MessageCommands to their string representation
var ProtocolMessageCommandToString = map[MessageCommand]string{
CmdVersion: "Version",
CmdVerAck: "VerAck",
CmdRequestAddresses: "RequestAddresses",
CmdAddresses: "Addresses",
CmdRequestIBDBlocks: "RequestBlocks",
CmdBlock: "Block",
CmdTx: "Tx",
CmdPing: "Ping",
CmdPong: "Pong",
CmdRequestBlockLocator: "RequestBlockLocator",
CmdBlockLocator: "BlockLocator",
CmdSelectedTip: "SelectedTip",
CmdRequestSelectedTip: "RequestSelectedTip",
CmdInvRelayBlock: "InvRelayBlock",
CmdRequestRelayBlocks: "RequestRelayBlocks",
CmdInvTransaction: "InvTransaction",
CmdRequestTransactions: "RequestTransactions",
CmdIBDBlock: "IBDBlock",
CmdRequestNextIBDBlocks: "RequestNextIBDBlocks",
CmdDoneIBDBlocks: "DoneIBDBlocks",
CmdTransactionNotFound: "TransactionNotFound",
CmdReject: "Reject",
CmdVersion: "Version",
CmdVerAck: "VerAck",
CmdRequestAddresses: "RequestAddresses",
CmdAddresses: "Addresses",
CmdRequestHeaders: "RequestHeaders",
CmdBlock: "Block",
CmdTx: "Tx",
CmdPing: "Ping",
CmdPong: "Pong",
CmdRequestBlockLocator: "RequestBlockLocator",
CmdBlockLocator: "BlockLocator",
CmdSelectedTip: "SelectedTip",
CmdRequestSelectedTip: "RequestSelectedTip",
CmdInvRelayBlock: "InvRelayBlock",
CmdRequestRelayBlocks: "RequestRelayBlocks",
CmdInvTransaction: "InvTransaction",
CmdRequestTransactions: "RequestTransactions",
CmdIBDBlock: "IBDBlock",
CmdDoneHeaders: "DoneHeaders",
CmdTransactionNotFound: "TransactionNotFound",
CmdReject: "Reject",
CmdHeader: "Header",
CmdRequestNextHeaders: "RequestNextHeaders",
CmdRequestIBDRootUTXOSetAndBlock: "RequestPruningUTXOSetAndBlock",
CmdIBDRootUTXOSetAndBlock: "IBDRootUTXOSetAndBlock",
CmdRequestIBDBlocks: "RequestIBDBlocks",
CmdIBDRootNotFound: "IBDRootNotFound",
}
// RPCMessageCommandToString maps all MessageCommands to their string representation

View File

@@ -0,0 +1,22 @@
package appmessage
// MsgIBDRootNotFound implements the Message interface and represents a kaspa
// IBDRootNotFound message. It is used to notify the IBD root that was requested
// by other peer was not found.
//
// This message has no payload.
type MsgIBDRootNotFound struct {
baseMessage
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgIBDRootNotFound) Command() MessageCommand {
return CmdIBDRootNotFound
}
// NewMsgIBDRootNotFound returns a new kaspa IBDRootNotFound message that conforms to the
// Message interface.
func NewMsgIBDRootNotFound() *MsgDoneHeaders {
return &MsgDoneHeaders{}
}

View File

@@ -4,59 +4,15 @@
package appmessage
import (
"fmt"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
)
// MaxAddressesPerMsg is the maximum number of addresses that can be in a single
// kaspa Addresses message (MsgAddresses).
const MaxAddressesPerMsg = 1000
// MsgAddresses implements the Message interface and represents a kaspa
// Addresses message. It is used to provide a list of known active peers on the
// network. An active peer is considered one that has transmitted a message
// within the last 3 hours. Nodes which have not transmitted in that time
// frame should be forgotten. Each message is limited to a maximum number of
// addresses, which is currently 1000. As a result, multiple messages must
// be used to relay the full list.
//
// Use the AddAddress function to build up the list of known addresses when
// sending an Addresses message to another peer.
// Addresses message.
type MsgAddresses struct {
baseMessage
IncludeAllSubnetworks bool
SubnetworkID *externalapi.DomainSubnetworkID
AddrList []*NetAddress
}
// AddAddress adds a known active peer to the message.
func (msg *MsgAddresses) AddAddress(na *NetAddress) error {
if len(msg.AddrList)+1 > MaxAddressesPerMsg {
str := fmt.Sprintf("too many addresses in message [max %d]",
MaxAddressesPerMsg)
return messageError("MsgAddresses.AddAddress", str)
}
msg.AddrList = append(msg.AddrList, na)
return nil
}
// AddAddresses adds multiple known active peers to the message.
func (msg *MsgAddresses) AddAddresses(netAddrs ...*NetAddress) error {
for _, na := range netAddrs {
err := msg.AddAddress(na)
if err != nil {
return err
}
}
return nil
}
// ClearAddresses removes all addresses from the message.
func (msg *MsgAddresses) ClearAddresses() {
msg.AddrList = []*NetAddress{}
AddressList []*NetAddress
}
// Command returns the protocol command string for the message. This is part
@@ -67,10 +23,8 @@ func (msg *MsgAddresses) Command() MessageCommand {
// NewMsgAddresses returns a new kaspa Addresses message that conforms to the
// Message interface. See MsgAddresses for details.
func NewMsgAddresses(includeAllSubnetworks bool, subnetworkID *externalapi.DomainSubnetworkID) *MsgAddresses {
func NewMsgAddresses(addressList []*NetAddress) *MsgAddresses {
return &MsgAddresses{
IncludeAllSubnetworks: includeAllSubnetworks,
SubnetworkID: subnetworkID,
AddrList: make([]*NetAddress, 0, MaxAddressesPerMsg),
AddressList: addressList,
}
}

View File

@@ -1,58 +0,0 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package appmessage
import (
"net"
"testing"
"github.com/davecgh/go-spew/spew"
)
// TestAddresses tests the MsgAddresses API.
func TestAddresses(t *testing.T) {
// Ensure the command is expected value.
wantCmd := MessageCommand(3)
msg := NewMsgAddresses(false, nil)
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgAddresses: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure NetAddresses are added properly.
tcpAddr := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 16111}
na := NewNetAddress(tcpAddr, SFNodeNetwork)
err := msg.AddAddress(na)
if err != nil {
t.Errorf("AddAddress: %v", err)
}
if msg.AddrList[0] != na {
t.Errorf("AddAddress: wrong address added - got %v, want %v",
spew.Sprint(msg.AddrList[0]), spew.Sprint(na))
}
// Ensure the address list is cleared properly.
msg.ClearAddresses()
if len(msg.AddrList) != 0 {
t.Errorf("ClearAddresses: address list is not empty - "+
"got %v [%v], want %v", len(msg.AddrList),
spew.Sprint(msg.AddrList[0]), 0)
}
// Ensure adding more than the max allowed addresses per message returns
// error.
for i := 0; i < MaxAddressesPerMsg+1; i++ {
err = msg.AddAddress(na)
}
if err == nil {
t.Errorf("AddAddress: expected error on too many addresses " +
"not received")
}
err = msg.AddAddresses(na)
if err == nil {
t.Errorf("AddAddresses: expected error on too many addresses " +
"not received")
}
}

View File

@@ -40,7 +40,7 @@ type TxLoc struct {
// response to a getdata message (MsgGetData) for a given block hash.
type MsgBlock struct {
baseMessage
Header BlockHeader
Header MsgBlockHeader
Transactions []*MsgTx
}
@@ -79,7 +79,7 @@ func (msg *MsgBlock) ConvertToPartial(subnetworkID *externalapi.DomainSubnetwork
// NewMsgBlock returns a new kaspa block message that conforms to the
// Message interface. See MsgBlock for details.
func NewMsgBlock(blockHeader *BlockHeader) *MsgBlock {
func NewMsgBlock(blockHeader *MsgBlockHeader) *MsgBlock {
return &MsgBlock{
Header: *blockHeader,
Transactions: make([]*MsgTx, 0, defaultTransactionAlloc),

View File

@@ -129,7 +129,7 @@ func TestConvertToPartial(t *testing.T) {
// blockOne is the first block in the mainnet block DAG.
var blockOne = MsgBlock{
Header: BlockHeader{
Header: MsgBlockHeader{
Version: 1,
ParentHashes: []*externalapi.DomainHash{mainnetGenesisHash, simnetGenesisHash},
HashMerkleRoot: mainnetGenesisMerkleRoot,

View File

@@ -31,9 +31,11 @@ const MaxNumParentBlocks = 255
// BaseBlockHeaderPayload + up to MaxNumParentBlocks hashes of parent blocks
const MaxBlockHeaderPayload = BaseBlockHeaderPayload + (MaxNumParentBlocks * externalapi.DomainHashSize)
// BlockHeader defines information about a block and is used in the kaspa
// MsgBlockHeader defines information about a block and is used in the kaspa
// block (MsgBlock) and headers (MsgHeader) messages.
type BlockHeader struct {
type MsgBlockHeader struct {
baseMessage
// Version of the block. This is not the same as the protocol version.
Version int32
@@ -61,7 +63,7 @@ type BlockHeader struct {
}
// NumParentBlocks return the number of entries in ParentHashes
func (h *BlockHeader) NumParentBlocks() byte {
func (h *MsgBlockHeader) NumParentBlocks() byte {
numParents := len(h.ParentHashes)
if numParents > math.MaxUint8 {
panic(errors.Errorf("number of parents is %d, which is more than one byte can fit", numParents))
@@ -70,24 +72,30 @@ func (h *BlockHeader) NumParentBlocks() byte {
}
// BlockHash computes the block identifier hash for the given block header.
func (h *BlockHeader) BlockHash() *externalapi.DomainHash {
func (h *MsgBlockHeader) BlockHash() *externalapi.DomainHash {
return consensusserialization.HeaderHash(BlockHeaderToDomainBlockHeader(h))
}
// IsGenesis returns true iff this block is a genesis block
func (h *BlockHeader) IsGenesis() bool {
func (h *MsgBlockHeader) IsGenesis() bool {
return h.NumParentBlocks() == 0
}
// NewBlockHeader returns a new BlockHeader using the provided version, previous
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (h *MsgBlockHeader) Command() MessageCommand {
return CmdHeader
}
// NewBlockHeader returns a new MsgBlockHeader using the provided version, previous
// block hash, hash merkle root, accepted ID merkle root, difficulty bits, and nonce used to generate the
// block with defaults or calclulated values for the remaining fields.
func NewBlockHeader(version int32, parentHashes []*externalapi.DomainHash, hashMerkleRoot *externalapi.DomainHash,
acceptedIDMerkleRoot *externalapi.DomainHash, utxoCommitment *externalapi.DomainHash, bits uint32, nonce uint64) *BlockHeader {
acceptedIDMerkleRoot *externalapi.DomainHash, utxoCommitment *externalapi.DomainHash, bits uint32, nonce uint64) *MsgBlockHeader {
// Limit the timestamp to one millisecond precision since the protocol
// doesn't support better.
return &BlockHeader{
return &MsgBlockHeader{
Version: version,
ParentHashes: parentHashes,
HashMerkleRoot: hashMerkleRoot,

View File

@@ -14,7 +14,7 @@ import (
"github.com/kaspanet/kaspad/util/random"
)
// TestBlockHeader tests the BlockHeader API.
// TestBlockHeader tests the MsgBlockHeader API.
func TestBlockHeader(t *testing.T) {
nonce, err := random.Uint64()
if err != nil {
@@ -52,7 +52,7 @@ func TestIsGenesis(t *testing.T) {
bits := uint32(0x1d00ffff)
timestamp := mstime.UnixMilliseconds(0x495fab29000)
baseBlockHdr := &BlockHeader{
baseBlockHdr := &MsgBlockHeader{
Version: 1,
ParentHashes: []*externalapi.DomainHash{mainnetGenesisHash, simnetGenesisHash},
HashMerkleRoot: mainnetGenesisMerkleRoot,
@@ -60,7 +60,7 @@ func TestIsGenesis(t *testing.T) {
Bits: bits,
Nonce: nonce,
}
genesisBlockHdr := &BlockHeader{
genesisBlockHdr := &MsgBlockHeader{
Version: 1,
ParentHashes: []*externalapi.DomainHash{},
HashMerkleRoot: mainnetGenesisMerkleRoot,
@@ -70,8 +70,8 @@ func TestIsGenesis(t *testing.T) {
}
tests := []struct {
in *BlockHeader // Block header to encode
isGenesis bool // Expected result for call of .IsGenesis
in *MsgBlockHeader // Block header to encode
isGenesis bool // Expected result for call of .IsGenesis
}{
{genesisBlockHdr, true},
{baseBlockHdr, false},
@@ -81,7 +81,7 @@ func TestIsGenesis(t *testing.T) {
for i, test := range tests {
isGenesis := test.in.IsGenesis()
if isGenesis != test.isGenesis {
t.Errorf("BlockHeader.IsGenesis: #%d got: %t, want: %t",
t.Errorf("MsgBlockHeader.IsGenesis: #%d got: %t, want: %t",
i, isGenesis, test.isGenesis)
}
}

View File

@@ -0,0 +1,22 @@
package appmessage
// MsgDoneHeaders implements the Message interface and represents a kaspa
// DoneHeaders message. It is used to notify the IBD syncing peer that the
// syncer sent all the requested headers.
//
// This message has no payload.
type MsgDoneHeaders struct {
baseMessage
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgDoneHeaders) Command() MessageCommand {
return CmdDoneHeaders
}
// NewMsgDoneHeaders returns a new kaspa DoneIBDBlocks message that conforms to the
// Message interface.
func NewMsgDoneHeaders() *MsgDoneHeaders {
return &MsgDoneHeaders{}
}

View File

@@ -1,22 +0,0 @@
package appmessage
// MsgDoneIBDBlocks implements the Message interface and represents a kaspa
// DoneIBDBlocks message. It is used to notify the IBD syncing peer that the
// syncer sent all the requested blocks.
//
// This message has no payload.
type MsgDoneIBDBlocks struct {
baseMessage
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgDoneIBDBlocks) Command() MessageCommand {
return CmdDoneIBDBlocks
}
// NewMsgDoneIBDBlocks returns a new kaspa DoneIBDBlocks message that conforms to the
// Message interface.
func NewMsgDoneIBDBlocks() *MsgDoneIBDBlocks {
return &MsgDoneIBDBlocks{}
}

View File

@@ -0,0 +1,23 @@
package appmessage
// MsgIBDRootUTXOSetAndBlock implements the Message interface and represents a kaspa
// IBDRootUTXOSetAndBlock message. It is used to answer RequestIBDRootUTXOSetAndBlock messages.
type MsgIBDRootUTXOSetAndBlock struct {
baseMessage
UTXOSet []byte
Block *MsgBlock
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgIBDRootUTXOSetAndBlock) Command() MessageCommand {
return CmdIBDRootUTXOSetAndBlock
}
// NewMsgIBDRootUTXOSetAndBlock returns a new MsgIBDRootUTXOSetAndBlock.
func NewMsgIBDRootUTXOSetAndBlock(utxoSet []byte, block *MsgBlock) *MsgIBDRootUTXOSetAndBlock {
return &MsgIBDRootUTXOSetAndBlock{
UTXOSet: utxoSet,
Block: block,
}
}

View File

@@ -0,0 +1,34 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package appmessage
import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
)
// MsgRequestHeaders implements the Message interface and represents a kaspa
// RequestHeaders message. It is used to request a list of blocks starting after the
// low hash and until the high hash.
type MsgRequestHeaders struct {
baseMessage
LowHash *externalapi.DomainHash
HighHash *externalapi.DomainHash
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgRequestHeaders) Command() MessageCommand {
return CmdRequestHeaders
}
// NewMsgRequstHeaders returns a new kaspa RequestHeaders message that conforms to the
// Message interface using the passed parameters and defaults for the remaining
// fields.
func NewMsgRequstHeaders(lowHash, highHash *externalapi.DomainHash) *MsgRequestHeaders {
return &MsgRequestHeaders{
LowHash: lowHash,
HighHash: highHash,
}
}

View File

@@ -10,7 +10,7 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
)
// TestRequstIBDBlocks tests the MsgRequestIBDBlocks API.
// TestRequstIBDBlocks tests the MsgRequestHeaders API.
func TestRequstIBDBlocks(t *testing.T) {
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
lowHash, err := hashes.FromString(hashStr)
@@ -25,16 +25,16 @@ func TestRequstIBDBlocks(t *testing.T) {
}
// Ensure we get the same data back out.
msg := NewMsgRequstIBDBlocks(lowHash, highHash)
msg := NewMsgRequstHeaders(lowHash, highHash)
if *msg.HighHash != *highHash {
t.Errorf("NewMsgRequstIBDBlocks: wrong high hash - got %v, want %v",
t.Errorf("NewMsgRequstHeaders: wrong high hash - got %v, want %v",
msg.HighHash, highHash)
}
// Ensure the command is expected value.
wantCmd := MessageCommand(4)
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgRequstIBDBlocks: wrong command - got %v want %v",
t.Errorf("NewMsgRequstHeaders: wrong command - got %v want %v",
cmd, wantCmd)
}
}

View File

@@ -1,20 +1,19 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package appmessage
import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
)
// MaxRequestIBDBlocksHashes is the maximum number of hashes that can
// be in a single RequestIBDBlocks message.
const MaxRequestIBDBlocksHashes = MaxInvPerMsg
// MsgRequestIBDBlocks implements the Message interface and represents a kaspa
// RequestIBDBlocks message. It is used to request a list of blocks starting after the
// low hash and until the high hash.
// RequestIBDBlocks message. It is used to request blocks as part of the IBD
// protocol.
type MsgRequestIBDBlocks struct {
baseMessage
LowHash *externalapi.DomainHash
HighHash *externalapi.DomainHash
Hashes []*externalapi.DomainHash
}
// Command returns the protocol command string for the message. This is part
@@ -23,12 +22,9 @@ func (msg *MsgRequestIBDBlocks) Command() MessageCommand {
return CmdRequestIBDBlocks
}
// NewMsgRequstIBDBlocks returns a new kaspa RequestIBDBlocks message that conforms to the
// Message interface using the passed parameters and defaults for the remaining
// fields.
func NewMsgRequstIBDBlocks(lowHash, highHash *externalapi.DomainHash) *MsgRequestIBDBlocks {
// NewMsgRequestIBDBlocks returns a new MsgRequestIBDBlocks.
func NewMsgRequestIBDBlocks(hashes []*externalapi.DomainHash) *MsgRequestIBDBlocks {
return &MsgRequestIBDBlocks{
LowHash: lowHash,
HighHash: highHash,
Hashes: hashes,
}
}

View File

@@ -0,0 +1,26 @@
package appmessage
import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
)
// MsgRequestIBDRootUTXOSetAndBlock implements the Message interface and represents a kaspa
// RequestIBDRootUTXOSetAndBlock message. It is used to request the UTXO set and block body
// of the IBD root block.
type MsgRequestIBDRootUTXOSetAndBlock struct {
baseMessage
IBDRoot *externalapi.DomainHash
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgRequestIBDRootUTXOSetAndBlock) Command() MessageCommand {
return CmdRequestIBDRootUTXOSetAndBlock
}
// NewMsgRequestIBDRootUTXOSetAndBlock returns a new MsgRequestIBDRootUTXOSetAndBlock.
func NewMsgRequestIBDRootUTXOSetAndBlock(ibdRoot *externalapi.DomainHash) *MsgRequestIBDRootUTXOSetAndBlock {
return &MsgRequestIBDRootUTXOSetAndBlock{
IBDRoot: ibdRoot,
}
}

View File

@@ -0,0 +1,22 @@
package appmessage
// MsgRequestNextHeaders implements the Message interface and represents a kaspa
// RequestNextHeaders message. It is used to notify the IBD syncer peer to send
// more headers.
//
// This message has no payload.
type MsgRequestNextHeaders struct {
baseMessage
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgRequestNextHeaders) Command() MessageCommand {
return CmdRequestNextHeaders
}
// NewMsgRequestNextHeaders returns a new kaspa RequestNextHeaders message that conforms to the
// Message interface.
func NewMsgRequestNextHeaders() *MsgRequestNextHeaders {
return &MsgRequestNextHeaders{}
}

View File

@@ -1,22 +0,0 @@
package appmessage
// MsgRequestNextIBDBlocks implements the Message interface and represents a kaspa
// RequestNextIBDBlocks message. It is used to notify the IBD syncer peer to send
// more blocks.
//
// This message has no payload.
type MsgRequestNextIBDBlocks struct {
baseMessage
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgRequestNextIBDBlocks) Command() MessageCommand {
return CmdRequestNextIBDBlocks
}
// NewMsgRequestNextIBDBlocks returns a new kaspa RequestNextIBDBlocks message that conforms to the
// Message interface.
func NewMsgRequestNextIBDBlocks() *MsgRequestNextIBDBlocks {
return &MsgRequestNextIBDBlocks{}
}

View File

@@ -4,9 +4,9 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
)
// MsgRequestRelayBlocksHashes is the maximum number of hashes that can
// MaxRequestRelayBlocksHashes is the maximum number of hashes that can
// be in a single RequestRelayBlocks message.
const MsgRequestRelayBlocksHashes = MaxInvPerMsg
const MaxRequestRelayBlocksHashes = MaxInvPerMsg
// MsgRequestRelayBlocks implements the Message interface and represents a kaspa
// RequestRelayBlocks message. It is used to request blocks as part of the block

View File

@@ -6,9 +6,10 @@ package appmessage
import (
"encoding/binary"
"math"
"strconv"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
@@ -19,38 +20,10 @@ import (
)
const (
// TxVersion is the current latest supported transaction version.
TxVersion = 1
// MaxTxInSequenceNum is the maximum sequence number the sequence field
// of a transaction input can be.
MaxTxInSequenceNum uint64 = math.MaxUint64
// MaxPrevOutIndex is the maximum index the index field of a previous
// outpoint can be.
MaxPrevOutIndex uint32 = 0xffffffff
// SequenceLockTimeDisabled is a flag that if set on a transaction
// input's sequence number, the sequence number will not be interpreted
// as a relative locktime.
SequenceLockTimeDisabled = 1 << 31
// SequenceLockTimeIsSeconds is a flag that if set on a transaction
// input's sequence number, the relative locktime has units of 512
// seconds.
SequenceLockTimeIsSeconds = 1 << 22
// SequenceLockTimeMask is a mask that extracts the relative locktime
// when masked against the transaction input sequence number.
SequenceLockTimeMask = 0x0000ffff
// SequenceLockTimeGranularity is the defined time based granularity
// for milliseconds-based relative time locks. When converting from milliseconds
// to a sequence number, the value is right shifted by this amount,
// therefore the granularity of relative time locks in 524288 or 2^19
// seconds. Enforced relative lock times are multiples of 524288 milliseconds.
SequenceLockTimeGranularity = 19
// defaultTxInOutAlloc is the default size used for the backing array for
// transaction inputs and outputs. The array will dynamically grow as needed,
// but this figure is intended to provide enough space for the number of
@@ -130,7 +103,7 @@ func NewTxIn(prevOut *Outpoint, signatureScript []byte) *TxIn {
return &TxIn{
PreviousOutpoint: *prevOut,
SignatureScript: signatureScript,
Sequence: MaxTxInSequenceNum,
Sequence: constants.MaxTxInSequenceNum,
}
}
@@ -163,7 +136,7 @@ type MsgTx struct {
LockTime uint64
SubnetworkID externalapi.DomainSubnetworkID
Gas uint64
PayloadHash *externalapi.DomainHash
PayloadHash externalapi.DomainHash
Payload []byte
}
@@ -310,9 +283,9 @@ func newMsgTx(version int32, txIn []*TxIn, txOut []*TxOut, subnetworkID *externa
txOut = make([]*TxOut, 0, defaultTxInOutAlloc)
}
var payloadHash *externalapi.DomainHash
var payloadHash externalapi.DomainHash
if *subnetworkID != subnetworks.SubnetworkIDNative {
payloadHash = hashes.HashData(payload)
payloadHash = *hashes.HashData(payload)
}
return &MsgTx{

View File

@@ -58,6 +58,7 @@ type BlockVerboseData struct {
Difficulty float64
ParentHashes []string
SelectedParentHash string
BlueScore uint64
}
// TransactionVerboseData holds verbose data about a transaction

View File

@@ -20,7 +20,8 @@ func NewGetPeerAddressesRequestMessage() *GetPeerAddressesRequestMessage {
// its respective RPC message
type GetPeerAddressesResponseMessage struct {
baseMessage
Addresses []*GetPeerAddressesKnownAddressMessage
Addresses []*GetPeerAddressesKnownAddressMessage
BannedAddresses []*GetPeerAddressesKnownAddressMessage
Error *RPCError
}
@@ -31,9 +32,10 @@ func (msg *GetPeerAddressesResponseMessage) Command() MessageCommand {
}
// NewGetPeerAddressesResponseMessage returns a instance of the message
func NewGetPeerAddressesResponseMessage(addresses []*GetPeerAddressesKnownAddressMessage) *GetPeerAddressesResponseMessage {
func NewGetPeerAddressesResponseMessage(addresses []*GetPeerAddressesKnownAddressMessage, bannedAddresses []*GetPeerAddressesKnownAddressMessage) *GetPeerAddressesResponseMessage {
return &GetPeerAddressesResponseMessage{
Addresses: addresses,
Addresses: addresses,
BannedAddresses: bannedAddresses,
}
}

View File

@@ -70,11 +70,6 @@ func (a *ComponentManager) Stop() {
log.Errorf("Error stopping the net adapter: %+v", err)
}
err = a.addressManager.Stop()
if err != nil {
log.Errorf("Error stopping address manager: %s", err)
}
return
}
@@ -92,10 +87,12 @@ func NewComponentManager(cfg *config.Config, db infrastructuredatabase.Database,
if err != nil {
return nil, err
}
addressManager, err := addressmanager.New(cfg, db)
addressManager, err := addressmanager.New(addressmanager.NewConfig(cfg))
if err != nil {
return nil, err
}
connectionManager, err := connmanager.New(cfg, netAdapter, addressManager)
if err != nil {
return nil, err
@@ -141,14 +138,14 @@ func (a *ComponentManager) maybeSeedFromDNS() {
// Kaspad uses a lookup of the dns seeder here. Since seeder returns
// IPs of nodes and not its own IP, we can not know real IP of
// source. So we'll take first returned address as source.
a.addressManager.AddAddresses(addresses, addresses[0], nil)
a.addressManager.AddAddresses(addresses...)
})
}
if a.cfg.GRPCSeed != "" {
dnsseed.SeedFromGRPC(a.cfg.NetParams(), a.cfg.GRPCSeed, appmessage.SFNodeNetwork, false, nil,
func(addresses []*appmessage.NetAddress) {
a.addressManager.AddAddresses(addresses, addresses[0], nil)
a.addressManager.AddAddresses(addresses...)
})
}
}

View File

@@ -22,7 +22,7 @@ var (
// LogBlock logs a new block blue score as an information message
// to show progress to the user. In order to prevent spam, it limits logging to
// one message every 10 seconds with duration and totals included.
func LogBlock(block *externalapi.DomainBlock) error {
func LogBlock(block *externalapi.DomainBlock) {
mtx.Lock()
defer mtx.Unlock()
@@ -32,7 +32,7 @@ func LogBlock(block *externalapi.DomainBlock) error {
now := mstime.Now()
duration := now.Sub(lastBlockLogTime)
if duration < time.Second*10 {
return nil
return
}
// Truncate the duration to 10s of milliseconds.
@@ -55,5 +55,4 @@ func LogBlock(block *externalapi.DomainBlock) error {
receivedLogBlocks = 0
receivedLogTx = 0
lastBlockLogTime = now
return nil
}

View File

@@ -1,6 +1,9 @@
package flowcontext
import (
"github.com/kaspanet/kaspad/app/protocol/blocklogger"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/pkg/errors"
"sync/atomic"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
@@ -14,12 +17,22 @@ import (
// relays newly unorphaned transactions and possibly rebroadcast
// manually added transactions when not in IBD.
func (f *FlowContext) OnNewBlock(block *externalapi.DomainBlock) error {
f.Domain().MiningManager().HandleNewBlockTransactions(block.Transactions)
unorphanedBlocks, err := f.UnorphanBlocks(block)
if err != nil {
return err
}
if f.onBlockAddedToDAGHandler != nil {
err := f.onBlockAddedToDAGHandler(block)
if err != nil {
return err
newBlocks := append([]*externalapi.DomainBlock{block}, unorphanedBlocks...)
for _, newBlock := range newBlocks {
blocklogger.LogBlock(block)
_ = f.Domain().MiningManager().HandleNewBlockTransactions(newBlock.Transactions)
if f.onBlockAddedToDAGHandler != nil {
err := f.onBlockAddedToDAGHandler(newBlock)
if err != nil {
return err
}
}
}
@@ -70,6 +83,10 @@ func (f *FlowContext) SharedRequestedBlocks() *blockrelay.SharedRequestedBlocks
func (f *FlowContext) AddBlock(block *externalapi.DomainBlock) error {
err := f.Domain().Consensus().ValidateAndInsertBlock(block)
if err != nil {
if errors.As(err, &ruleerrors.RuleError{}) {
log.Infof("Validation failed for block %s: %s", consensusserialization.BlockHash(block), err)
return nil
}
return err
}
err = f.OnNewBlock(block)

View File

@@ -51,6 +51,9 @@ type FlowContext struct {
peers map[id.ID]*peerpkg.Peer
peersMutex sync.RWMutex
orphans map[externalapi.DomainHash]*externalapi.DomainBlock
orphansMutex sync.RWMutex
}
// New returns a new instance of FlowContext.
@@ -67,6 +70,7 @@ func New(cfg *config.Config, domain domain.Domain, addressManager *addressmanage
sharedRequestedBlocks: blockrelay.NewSharedRequestedBlocks(),
peers: make(map[id.ID]*peerpkg.Peer),
transactionsToRebroadcast: make(map[externalapi.DomainTransactionID]*externalapi.DomainTransaction),
orphans: make(map[externalapi.DomainHash]*externalapi.DomainBlock),
}
}

View File

@@ -1,6 +1,7 @@
package flowcontext
import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"sync/atomic"
"time"
@@ -19,7 +20,16 @@ func (f *FlowContext) StartIBDIfRequired() error {
return nil
}
peer, err := f.selectPeerForIBD()
syncInfo, err := f.domain.Consensus().GetSyncInfo()
if err != nil {
return err
}
if syncInfo.State == externalapi.SyncStateRelay {
return nil
}
peer, err := f.selectPeerForIBD(syncInfo)
if err != nil {
return err
}
@@ -42,18 +52,30 @@ func (f *FlowContext) IsInIBD() bool {
// selectPeerForIBD returns the first peer whose selected tip
// hash is not in our DAG
func (f *FlowContext) selectPeerForIBD() (*peerpkg.Peer, error) {
func (f *FlowContext) selectPeerForIBD(syncInfo *externalapi.SyncInfo) (*peerpkg.Peer, error) {
f.peersMutex.RLock()
defer f.peersMutex.RUnlock()
for _, peer := range f.peers {
peerSelectedTipHash := peer.SelectedTipHash()
if f.IsOrphan(peerSelectedTipHash) {
continue
}
blockInfo, err := f.domain.Consensus().GetBlockInfo(peerSelectedTipHash)
if err != nil {
return nil, err
}
if !blockInfo.Exists {
return peer, nil
if syncInfo.State == externalapi.SyncStateHeadersFirst {
if !blockInfo.Exists {
return peer, nil
}
} else {
if blockInfo.Exists && blockInfo.BlockStatus == externalapi.StatusHeaderOnly &&
blockInfo.IsBlockInHeaderPruningPointFuture {
return peer, nil
}
}
}
return nil, nil

View File

@@ -0,0 +1,130 @@
package flowcontext
import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
"github.com/pkg/errors"
)
// AddOrphan adds the block to the orphan set
func (f *FlowContext) AddOrphan(orphanBlock *externalapi.DomainBlock) {
f.orphansMutex.Lock()
defer f.orphansMutex.Unlock()
orphanHash := consensusserialization.BlockHash(orphanBlock)
f.orphans[*orphanHash] = orphanBlock
log.Infof("Received a block with missing parents, adding to orphan pool: %s", orphanHash)
}
// IsOrphan returns whether the given blockHash belongs to an orphan block
func (f *FlowContext) IsOrphan(blockHash *externalapi.DomainHash) bool {
f.orphansMutex.RLock()
defer f.orphansMutex.RUnlock()
_, ok := f.orphans[*blockHash]
return ok
}
// UnorphanBlocks removes the block from the orphan set, and remove all of the blocks that are not orphans anymore.
func (f *FlowContext) UnorphanBlocks(rootBlock *externalapi.DomainBlock) ([]*externalapi.DomainBlock, error) {
f.orphansMutex.Lock()
defer f.orphansMutex.Unlock()
// Find all the children of rootBlock among the orphans
// and add them to the process queue
rootBlockHash := consensusserialization.BlockHash(rootBlock)
processQueue := f.addChildOrphansToProcessQueue(rootBlockHash, []externalapi.DomainHash{})
var unorphanedBlocks []*externalapi.DomainBlock
for len(processQueue) > 0 {
var orphanHash externalapi.DomainHash
orphanHash, processQueue = processQueue[0], processQueue[1:]
orphanBlock := f.orphans[orphanHash]
log.Tracef("Considering to unorphan block %s with parents %s",
orphanHash, orphanBlock.Header.ParentHashes)
canBeUnorphaned := true
for _, orphanBlockParentHash := range orphanBlock.Header.ParentHashes {
orphanBlockParentInfo, err := f.domain.Consensus().GetBlockInfo(orphanBlockParentHash)
if err != nil {
return nil, err
}
if !orphanBlockParentInfo.Exists {
log.Tracef("Cannot unorphan block %s. It's missing at "+
"least the following parent: %s", orphanHash, orphanBlockParentHash)
canBeUnorphaned = false
break
}
}
if canBeUnorphaned {
err := f.unorphanBlock(orphanHash)
if err != nil {
return nil, err
}
unorphanedBlocks = append(unorphanedBlocks, orphanBlock)
processQueue = f.addChildOrphansToProcessQueue(&orphanHash, processQueue)
}
}
return unorphanedBlocks, nil
}
// addChildOrphansToProcessQueue finds all child orphans of `blockHash`
// and adds them to the given `processQueue` if they don't already exist
// inside of it
// Note that this method does not modify the given `processQueue`
func (f *FlowContext) addChildOrphansToProcessQueue(blockHash *externalapi.DomainHash,
processQueue []externalapi.DomainHash) []externalapi.DomainHash {
blockChildren := f.findChildOrphansOfBlock(blockHash)
for _, blockChild := range blockChildren {
exists := false
for _, queueOrphan := range processQueue {
if queueOrphan == blockChild {
exists = true
break
}
}
if !exists {
processQueue = append(processQueue, blockChild)
}
}
return processQueue
}
func (f *FlowContext) findChildOrphansOfBlock(blockHash *externalapi.DomainHash) []externalapi.DomainHash {
var childOrphans []externalapi.DomainHash
for orphanHash, orphanBlock := range f.orphans {
for _, orphanBlockParentHash := range orphanBlock.Header.ParentHashes {
if *orphanBlockParentHash == *blockHash {
childOrphans = append(childOrphans, orphanHash)
break
}
}
}
return childOrphans
}
func (f *FlowContext) unorphanBlock(orphanHash externalapi.DomainHash) error {
orphanBlock, ok := f.orphans[orphanHash]
if !ok {
return errors.Errorf("attempted to unorphan a non-orphan block %s", orphanHash)
}
delete(f.orphans, orphanHash)
err := f.domain.Consensus().ValidateAndInsertBlock(orphanBlock)
if err != nil {
if errors.As(err, &ruleerrors.RuleError{}) {
log.Infof("Validation failed for orphan block %s: %s", orphanHash, err)
return nil
}
return err
}
log.Infof("Unorphaned block %s", orphanHash)
return nil
}

View File

@@ -20,10 +20,6 @@ type ReceiveAddressesContext interface {
func ReceiveAddresses(context ReceiveAddressesContext, incomingRoute *router.Route, outgoingRoute *router.Route,
peer *peerpkg.Peer) error {
if !context.AddressManager().NeedMoreAddresses() {
return nil
}
subnetworkID := peer.SubnetworkID()
msgGetAddresses := appmessage.NewMsgRequestAddresses(false, subnetworkID)
err := outgoingRoute.Enqueue(msgGetAddresses)
@@ -37,21 +33,10 @@ func ReceiveAddresses(context ReceiveAddressesContext, incomingRoute *router.Rou
}
msgAddresses := message.(*appmessage.MsgAddresses)
if len(msgAddresses.AddrList) > addressmanager.GetAddressesMax {
if len(msgAddresses.AddressList) > addressmanager.GetAddressesMax {
return protocolerrors.Errorf(true, "address count exceeded %d", addressmanager.GetAddressesMax)
}
if msgAddresses.IncludeAllSubnetworks {
return protocolerrors.Errorf(true, "got unexpected "+
"IncludeAllSubnetworks=true in [%s] command", msgAddresses.Command())
}
if msgAddresses.SubnetworkID != nil && *msgAddresses.SubnetworkID != *context.Config().SubnetworkID {
return protocolerrors.Errorf(false, "only full nodes and %s subnetwork IDs "+
"are allowed in [%s] command, but got subnetwork ID %s",
context.Config().SubnetworkID, msgAddresses.Command(), msgAddresses.SubnetworkID)
}
sourceAddress := peer.Connection().NetAddress()
context.AddressManager().AddAddresses(msgAddresses.AddrList, sourceAddress, msgAddresses.SubnetworkID)
context.AddressManager().AddAddresses(msgAddresses.AddressList...)
return nil
}

View File

@@ -1,10 +1,12 @@
package addressexchange
import (
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
"math/rand"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"math/rand"
)
// SendAddressesContext is the interface for the context needed for the SendAddresses flow.
@@ -14,21 +16,25 @@ type SendAddressesContext interface {
// SendAddresses sends addresses to a peer that requests it.
func SendAddresses(context SendAddressesContext, incomingRoute *router.Route, outgoingRoute *router.Route) error {
message, err := incomingRoute.Dequeue()
if err != nil {
return err
}
for {
message, err := incomingRoute.Dequeue()
if err != nil {
return err
}
msgGetAddresses := message.(*appmessage.MsgRequestAddresses)
addresses := context.AddressManager().AddressCache(msgGetAddresses.IncludeAllSubnetworks,
msgGetAddresses.SubnetworkID)
msgAddresses := appmessage.NewMsgAddresses(msgGetAddresses.IncludeAllSubnetworks, msgGetAddresses.SubnetworkID)
err = msgAddresses.AddAddresses(shuffleAddresses(addresses)...)
if err != nil {
return err
}
_, ok := message.(*appmessage.MsgRequestAddresses)
if !ok {
return protocolerrors.Errorf(true, "unexpected message. "+
"Expected: %s, got: %s", appmessage.CmdRequestAddresses, message.Command())
}
addresses := context.AddressManager().Addresses()
msgAddresses := appmessage.NewMsgAddresses(shuffleAddresses(addresses))
return outgoingRoute.Enqueue(msgAddresses)
err = outgoingRoute.Enqueue(msgAddresses)
if err != nil {
return err
}
}
}
// shuffleAddresses randomizes the given addresses sent if there are more than the maximum allowed in one message.

View File

@@ -2,7 +2,6 @@ package blockrelay
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/protocol/blocklogger"
"github.com/kaspanet/kaspad/app/protocol/common"
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
@@ -26,6 +25,8 @@ type RelayInvsContext interface {
StartIBDIfRequired() error
IsInIBD() bool
Broadcast(message appmessage.Message) error
AddOrphan(orphanBlock *externalapi.DomainBlock)
IsOrphan(blockHash *externalapi.DomainHash) bool
}
type handleRelayInvsFlow struct {
@@ -71,6 +72,10 @@ func (flow *handleRelayInvsFlow) start() error {
continue
}
if flow.IsOrphan(inv.Hash) {
continue
}
err = flow.StartIBDIfRequired()
if err != nil {
return err
@@ -93,7 +98,6 @@ func (flow *handleRelayInvsFlow) start() error {
}
func (flow *handleRelayInvsFlow) readInv() (*appmessage.MsgInvRelayBlock, error) {
if len(flow.invsQueue) > 0 {
var inv *appmessage.MsgInvRelayBlock
inv, flow.invsQueue = flow.invsQueue[0], flow.invsQueue[1:]
@@ -114,7 +118,7 @@ func (flow *handleRelayInvsFlow) readInv() (*appmessage.MsgInvRelayBlock, error)
}
func (flow *handleRelayInvsFlow) requestBlocks(requestQueue *hashesQueueSet) error {
numHashesToRequest := mathUtil.MinInt(appmessage.MsgRequestRelayBlocksHashes, requestQueue.len())
numHashesToRequest := mathUtil.MinInt(appmessage.MaxRequestRelayBlocksHashes, requestQueue.len())
hashesToRequest := requestQueue.dequeue(numHashesToRequest)
pendingBlocks := map[externalapi.DomainHash]struct{}{}
@@ -180,9 +184,7 @@ func (flow *handleRelayInvsFlow) requestBlocks(requestQueue *hashesQueueSet) err
// readMsgBlock returns the next msgBlock in msgChan, and populates invsQueue with any inv messages that meanwhile arrive.
//
// Note: this function assumes msgChan can contain only appmessage.MsgInvRelayBlock and appmessage.MsgBlock messages.
func (flow *handleRelayInvsFlow) readMsgBlock() (
msgBlock *appmessage.MsgBlock, err error) {
func (flow *handleRelayInvsFlow) readMsgBlock() (msgBlock *appmessage.MsgBlock, err error) {
for {
message, err := flow.incomingRoute.DequeueWithTimeout(common.DefaultTimeout)
if err != nil {
@@ -222,6 +224,10 @@ func (flow *handleRelayInvsFlow) processAndRelayBlock(requestQueue *hashesQueueS
return err
}
selectedTipBlueScore, err := blocks.ExtractBlueScore(virtualSelectedParent)
if err != nil {
return err
}
if blueScore > selectedTipBlueScore+maxOrphanBlueScoreDiff {
log.Infof("Orphan block %s has blue score %d and the selected tip blue score is "+
"%d. Ignoring orphans with a blue score difference from the selected tip greater than %d",
@@ -229,6 +235,9 @@ func (flow *handleRelayInvsFlow) processAndRelayBlock(requestQueue *hashesQueueS
return nil
}
// Add the orphan to the orphan pool
flow.AddOrphan(block)
// Request the parents for the orphan block from the peer that sent it.
for _, missingAncestor := range missingParentsError.MissingParentHashes {
requestQueue.enqueueIfNotExists(missingAncestor)
@@ -240,15 +249,13 @@ func (flow *handleRelayInvsFlow) processAndRelayBlock(requestQueue *hashesQueueS
return protocolerrors.Wrapf(true, err, "got invalid block %s from relay", blockHash)
}
err = blocklogger.LogBlock(block)
if err != nil {
return err
}
err = flow.Broadcast(appmessage.NewMsgInvBlock(blockHash))
if err != nil {
return err
}
log.Infof("Accepted block %s via relay", blockHash)
err = flow.StartIBDIfRequired()
if err != nil {
return err

View File

@@ -1,7 +1,6 @@
package handshake
import (
"sync"
"sync/atomic"
"github.com/kaspanet/kaspad/domain"
@@ -16,7 +15,6 @@ import (
"github.com/kaspanet/kaspad/app/appmessage"
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
routerpkg "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/kaspanet/kaspad/util/locks"
"github.com/pkg/errors"
)
@@ -38,10 +36,12 @@ func HandleHandshake(context HandleHandshakeContext, netConnection *netadapter.N
) (*peerpkg.Peer, error) {
// For HandleHandshake to finish, we need to get from the other node
// a version and verack messages, so we increase the wait group by 2
// and block HandleHandshake with wg.Wait().
wg := sync.WaitGroup{}
wg.Add(2)
// a version and verack messages, so we set doneCount to 2, decrease it
// when sending and receiving the version, and close the doneChan when
// it's 0. Then we wait for on select for a tick from doneChan or from
// errChan.
doneCount := int32(2)
doneChan := make(chan struct{})
isStopping := uint32(0)
errChan := make(chan error)
@@ -56,7 +56,9 @@ func HandleHandshake(context HandleHandshakeContext, netConnection *netadapter.N
return
}
peerAddress = address
wg.Done()
if atomic.AddInt32(&doneCount, -1) == 0 {
close(doneChan)
}
})
spawn("HandleHandshake-SendVersion", func() {
@@ -65,7 +67,9 @@ func HandleHandshake(context HandleHandshakeContext, netConnection *netadapter.N
handleError(err, "SendVersion", &isStopping, errChan)
return
}
wg.Done()
if atomic.AddInt32(&doneCount, -1) == 0 {
close(doneChan)
}
})
select {
@@ -74,7 +78,7 @@ func HandleHandshake(context HandleHandshakeContext, netConnection *netadapter.N
return nil, err
}
return nil, nil
case <-locks.ReceiveFromChanWhenDone(func() { wg.Wait() }):
case <-doneChan:
}
err := context.AddToPeers(peer)
@@ -86,9 +90,7 @@ func HandleHandshake(context HandleHandshakeContext, netConnection *netadapter.N
}
if peerAddress != nil {
subnetworkID := peer.SubnetworkID()
context.AddressManager().AddAddress(peerAddress, peerAddress, subnetworkID)
context.AddressManager().Good(peerAddress, subnetworkID)
context.AddressManager().AddAddresses(peerAddress)
}
err = context.StartIBDIfRequired()

View File

@@ -56,7 +56,7 @@ func (flow *sendVersionFlow) start() error {
subnetworkID := flow.Config().SubnetworkID
// Version message.
localAddress := flow.AddressManager().GetBestLocalAddress(flow.peer.Connection().NetAddress())
localAddress := flow.AddressManager().BestLocalAddress(flow.peer.Connection().NetAddress())
msg := appmessage.NewMsgVersion(localAddress, flow.NetAdapter().ID(),
flow.Config().ActiveNetParams.Name, selectedTipHash, subnetworkID)
msg.AddUserAgent(userAgentName, userAgentVersion, flow.Config().UserAgentComments...)

View File

@@ -0,0 +1,51 @@
package ibd
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
"github.com/kaspanet/kaspad/domain"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/pkg/errors"
)
// HandleIBDBlockRequestsContext is the interface for the context needed for the HandleIBDBlockRequests flow.
type HandleIBDBlockRequestsContext interface {
Domain() domain.Domain
}
// HandleIBDBlockRequests listens to appmessage.MsgRequestRelayBlocks messages and sends
// their corresponding blocks to the requesting peer.
func HandleIBDBlockRequests(context HandleIBDBlockRequestsContext, incomingRoute *router.Route,
outgoingRoute *router.Route) error {
for {
message, err := incomingRoute.Dequeue()
if err != nil {
return err
}
msgRequestIBDBlocks := message.(*appmessage.MsgRequestIBDBlocks)
for _, hash := range msgRequestIBDBlocks.Hashes {
// Fetch the block from the database.
blockInfo, err := context.Domain().Consensus().GetBlockInfo(hash)
if err != nil {
return err
}
if !blockInfo.Exists {
return protocolerrors.Errorf(true, "block %s not found", hash)
}
block, err := context.Domain().Consensus().GetBlock(hash)
if err != nil {
return errors.Wrapf(err, "unable to fetch requested block hash %s", hash)
}
// TODO (Partial nodes): Convert block to partial block if needed
blockMessage := appmessage.DomainBlockToMsgBlock(block)
ibdBlockMessage := appmessage.NewMsgIBDBlock(blockMessage)
err = outgoingRoute.Enqueue(ibdBlockMessage)
if err != nil {
return err
}
}
}
}

View File

@@ -10,8 +10,9 @@ import (
)
const ibdBatchSize = router.DefaultMaxMessages
const maxHeaders = appmessage.MaxInvPerMsg
// RequestIBDBlocksContext is the interface for the context needed for the HandleRequestIBDBlocks flow.
// RequestIBDBlocksContext is the interface for the context needed for the HandleRequestHeaders flow.
type RequestIBDBlocksContext interface {
Domain() domain.Domain
}
@@ -21,8 +22,8 @@ type handleRequestBlocksFlow struct {
incomingRoute, outgoingRoute *router.Route
}
// HandleRequestIBDBlocks handles getBlocks messages
func HandleRequestIBDBlocks(context RequestIBDBlocksContext, incomingRoute *router.Route, outgoingRoute *router.Route) error {
// HandleRequestHeaders handles RequestHeaders messages
func HandleRequestHeaders(context RequestIBDBlocksContext, incomingRoute *router.Route, outgoingRoute *router.Route) error {
flow := &handleRequestBlocksFlow{
RequestIBDBlocksContext: context,
incomingRoute: incomingRoute,
@@ -33,24 +34,24 @@ func HandleRequestIBDBlocks(context RequestIBDBlocksContext, incomingRoute *rout
func (flow *handleRequestBlocksFlow) start() error {
for {
lowHash, highHash, err := receiveRequestIBDBlocks(flow.incomingRoute)
lowHash, highHash, err := receiveRequestHeaders(flow.incomingRoute)
if err != nil {
return err
}
msgIBDBlocks, err := flow.buildMsgIBDBlocks(lowHash, highHash)
msgHeaders, err := flow.buildMsgBlockHeaders(lowHash, highHash)
if err != nil {
return err
}
for offset := 0; offset < len(msgIBDBlocks); offset += ibdBatchSize {
for offset := 0; offset < len(msgHeaders); offset += ibdBatchSize {
end := offset + ibdBatchSize
if end > len(msgIBDBlocks) {
end = len(msgIBDBlocks)
if end > len(msgHeaders) {
end = len(msgHeaders)
}
blocksToSend := msgIBDBlocks[offset:end]
err = flow.sendMsgIBDBlocks(blocksToSend)
blocksToSend := msgHeaders[offset:end]
err = flow.sendHeaders(blocksToSend)
if err != nil {
return nil
}
@@ -66,57 +67,57 @@ func (flow *handleRequestBlocksFlow) start() error {
return err
}
if _, ok := message.(*appmessage.MsgRequestNextIBDBlocks); !ok {
if _, ok := message.(*appmessage.MsgRequestNextHeaders); !ok {
return protocolerrors.Errorf(true, "received unexpected message type. "+
"expected: %s, got: %s", appmessage.CmdRequestNextIBDBlocks, message.Command())
"expected: %s, got: %s", appmessage.CmdRequestNextHeaders, message.Command())
}
}
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgDoneIBDBlocks())
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgDoneHeaders())
if err != nil {
return err
}
}
}
func receiveRequestIBDBlocks(incomingRoute *router.Route) (lowHash *externalapi.DomainHash,
func receiveRequestHeaders(incomingRoute *router.Route) (lowHash *externalapi.DomainHash,
highHash *externalapi.DomainHash, err error) {
message, err := incomingRoute.Dequeue()
if err != nil {
return nil, nil, err
}
msgRequestIBDBlocks := message.(*appmessage.MsgRequestIBDBlocks)
msgRequestIBDBlocks := message.(*appmessage.MsgRequestHeaders)
return msgRequestIBDBlocks.LowHash, msgRequestIBDBlocks.HighHash, nil
}
func (flow *handleRequestBlocksFlow) buildMsgIBDBlocks(lowHash *externalapi.DomainHash,
highHash *externalapi.DomainHash) ([]*appmessage.MsgIBDBlock, error) {
func (flow *handleRequestBlocksFlow) buildMsgBlockHeaders(lowHash *externalapi.DomainHash,
highHash *externalapi.DomainHash) ([]*appmessage.MsgBlockHeader, error) {
blockHashes, err := flow.Domain().Consensus().GetHashesBetween(lowHash, highHash)
if err != nil {
return nil, err
}
const maxHashesInMsgIBDBlocks = appmessage.MaxInvPerMsg
if len(blockHashes) > maxHashesInMsgIBDBlocks {
blockHashes = blockHashes[:maxHashesInMsgIBDBlocks]
if len(blockHashes) > maxHeaders {
blockHashes = blockHashes[:maxHeaders]
}
msgIBDBlocks := make([]*appmessage.MsgIBDBlock, len(blockHashes))
msgBlockHeaders := make([]*appmessage.MsgBlockHeader, len(blockHashes))
for i, blockHash := range blockHashes {
block, err := flow.Domain().Consensus().GetBlock(blockHash)
header, err := flow.Domain().Consensus().GetBlockHeader(blockHash)
if err != nil {
return nil, err
}
msgIBDBlocks[i] = appmessage.NewMsgIBDBlock(appmessage.DomainBlockToMsgBlock(block))
msgBlockHeaders[i] = appmessage.DomainBlockHeaderToBlockHeader(header)
}
return msgIBDBlocks, nil
return msgBlockHeaders, nil
}
func (flow *handleRequestBlocksFlow) sendMsgIBDBlocks(msgIBDBlocks []*appmessage.MsgIBDBlock) error {
for _, msgIBDBlock := range msgIBDBlocks {
err := flow.outgoingRoute.Enqueue(msgIBDBlock)
func (flow *handleRequestBlocksFlow) sendHeaders(headers []*appmessage.MsgBlockHeader) error {
for _, msgBlockHeader := range headers {
err := flow.outgoingRoute.Enqueue(msgBlockHeader)
if err != nil {
return err
}

View File

@@ -0,0 +1,15 @@
package ibd
import (
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
"github.com/kaspanet/kaspad/domain/dagconfig"
"testing"
)
func TestMaxHeaders(t *testing.T) {
testutils.ForAllNets(t, false, func(t *testing.T, params *dagconfig.Params) {
if params.FinalityDepth() > maxHeaders {
t.Errorf("FinalityDepth() in %s should be lower or equal to appmessage.MaxInvPerMsg", params.Name)
}
})
}

View File

@@ -0,0 +1,65 @@
package ibd
import (
"errors"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/domain"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
)
// HandleRequestIBDRootUTXOSetAndBlockContext is the interface for the context needed for the HandleRequestIBDRootUTXOSetAndBlock flow.
type HandleRequestIBDRootUTXOSetAndBlockContext interface {
Domain() domain.Domain
}
type handleRequestIBDRootUTXOSetAndBlockFlow struct {
HandleRequestIBDRootUTXOSetAndBlockContext
incomingRoute, outgoingRoute *router.Route
}
// HandleRequestIBDRootUTXOSetAndBlock listens to appmessage.MsgRequestIBDRootUTXOSetAndBlock messages and sends
// the IBD root UTXO set and block body.
func HandleRequestIBDRootUTXOSetAndBlock(context HandleRequestIBDRootUTXOSetAndBlockContext, incomingRoute,
outgoingRoute *router.Route) error {
flow := &handleRequestIBDRootUTXOSetAndBlockFlow{
HandleRequestIBDRootUTXOSetAndBlockContext: context,
incomingRoute: incomingRoute,
outgoingRoute: outgoingRoute,
}
return flow.start()
}
func (flow *handleRequestIBDRootUTXOSetAndBlockFlow) start() error {
for {
message, err := flow.incomingRoute.Dequeue()
if err != nil {
return err
}
msgRequestIBDRootUTXOSetAndBlock := message.(*appmessage.MsgRequestIBDRootUTXOSetAndBlock)
utxoSet, err := flow.Domain().Consensus().GetPruningPointUTXOSet(msgRequestIBDRootUTXOSetAndBlock.IBDRoot)
if err != nil {
if errors.Is(err, ruleerrors.ErrWrongPruningPointHash) {
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgIBDRootNotFound())
if err != nil {
return err
}
continue
}
}
block, err := flow.Domain().Consensus().GetBlock(msgRequestIBDRootUTXOSetAndBlock.IBDRoot)
if err != nil {
return err
}
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgIBDRootUTXOSetAndBlock(utxoSet,
appmessage.DomainBlockToMsgBlock(block)))
if err != nil {
return err
}
}
}

View File

@@ -2,7 +2,6 @@ package ibd
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/protocol/blocklogger"
"github.com/kaspanet/kaspad/app/protocol/common"
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
@@ -53,8 +52,53 @@ func (flow *handleIBDFlow) start() error {
func (flow *handleIBDFlow) runIBD() error {
flow.peer.WaitForIBDStart()
defer flow.FinishIBD()
err := flow.ibdLoop()
if err != nil {
finishIBDErr := flow.FinishIBD()
if finishIBDErr != nil {
return finishIBDErr
}
return err
}
return flow.FinishIBD()
}
func (flow *handleIBDFlow) ibdLoop() error {
for {
syncInfo, err := flow.Domain().Consensus().GetSyncInfo()
if err != nil {
return err
}
switch syncInfo.State {
case externalapi.SyncStateHeadersFirst:
err := flow.syncHeaders()
if err != nil {
return err
}
case externalapi.SyncStateMissingUTXOSet:
found, err := flow.fetchMissingUTXOSet(syncInfo.IBDRootUTXOBlockHash)
if err != nil {
return err
}
if !found {
return nil
}
case externalapi.SyncStateMissingBlockBodies:
err := flow.syncMissingBlockBodies()
if err != nil {
return err
}
case externalapi.SyncStateRelay:
return nil
default:
return errors.Errorf("unexpected state %s", syncInfo.State)
}
}
}
func (flow *handleIBDFlow) syncHeaders() error {
peerSelectedTipHash := flow.peer.SelectedTipHash()
log.Debugf("Trying to find highest shared chain block with peer %s with selected tip %s", flow.peer, peerSelectedTipHash)
highestSharedBlockHash, err := flow.findHighestSharedBlockHash(peerSelectedTipHash)
@@ -64,7 +108,108 @@ func (flow *handleIBDFlow) runIBD() error {
log.Debugf("Found highest shared chain block %s with peer %s", highestSharedBlockHash, flow.peer)
return flow.downloadBlocks(highestSharedBlockHash, peerSelectedTipHash)
return flow.downloadHeaders(highestSharedBlockHash, peerSelectedTipHash)
}
func (flow *handleIBDFlow) syncMissingBlockBodies() error {
hashes, err := flow.Domain().Consensus().GetMissingBlockBodyHashes(flow.peer.SelectedTipHash())
if err != nil {
return err
}
for offset := 0; offset < len(hashes); offset += appmessage.MaxRequestIBDBlocksHashes {
var hashesToRequest []*externalapi.DomainHash
if offset+appmessage.MaxRequestIBDBlocksHashes < len(hashes) {
hashesToRequest = hashes[offset : offset+appmessage.MaxRequestIBDBlocksHashes]
} else {
hashesToRequest = hashes[offset:]
}
err := flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestIBDBlocks(hashesToRequest))
if err != nil {
return err
}
for _, expectedHash := range hashesToRequest {
message, err := flow.incomingRoute.DequeueWithTimeout(common.DefaultTimeout)
if err != nil {
return err
}
msgIBDBlock, ok := message.(*appmessage.MsgIBDBlock)
if !ok {
return protocolerrors.Errorf(true, "received unexpected message type. "+
"expected: %s, got: %s", appmessage.CmdIBDBlock, message.Command())
}
block := appmessage.MsgBlockToDomainBlock(msgIBDBlock.MsgBlock)
blockHash := consensusserialization.BlockHash(block)
if *expectedHash != *blockHash {
return protocolerrors.Errorf(true, "expected block %s but got %s", expectedHash, blockHash)
}
err = flow.Domain().Consensus().ValidateAndInsertBlock(block)
if err != nil {
return protocolerrors.ConvertToBanningProtocolErrorIfRuleError(err, "invalid block %s", blockHash)
}
err = flow.OnNewBlock(block)
if err != nil {
return err
}
}
}
return nil
}
func (flow *handleIBDFlow) fetchMissingUTXOSet(ibdRootHash *externalapi.DomainHash) (bool, error) {
err := flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestIBDRootUTXOSetAndBlock(ibdRootHash))
if err != nil {
return false, err
}
utxoSet, block, found, err := flow.receiveIBDRootUTXOSetAndBlock()
if err != nil {
return false, err
}
if !found {
return false, nil
}
err = flow.Domain().Consensus().ValidateAndInsertBlock(block)
if err != nil {
blockHash := consensusserialization.BlockHash(block)
return false, protocolerrors.ConvertToBanningProtocolErrorIfRuleError(err, "got invalid block %s during IBD", blockHash)
}
err = flow.Domain().Consensus().SetPruningPointUTXOSet(utxoSet)
if err != nil {
return false, protocolerrors.ConvertToBanningProtocolErrorIfRuleError(err, "error with IBD root UTXO set")
}
return true, nil
}
func (flow *handleIBDFlow) receiveIBDRootUTXOSetAndBlock() ([]byte, *externalapi.DomainBlock, bool, error) {
message, err := flow.incomingRoute.DequeueWithTimeout(common.DefaultTimeout)
if err != nil {
return nil, nil, false, err
}
switch message := message.(type) {
case *appmessage.MsgIBDRootUTXOSetAndBlock:
return message.UTXOSet,
appmessage.MsgBlockToDomainBlock(message.Block), true, nil
case *appmessage.MsgIBDRootNotFound:
return nil, nil, false, nil
default:
return nil, nil, false,
protocolerrors.Errorf(true, "received unexpected message type. "+
"expected: %s or %s, got: %s",
appmessage.CmdIBDRootUTXOSetAndBlock, appmessage.CmdIBDRootNotFound, message.Command(),
)
}
}
func (flow *handleIBDFlow) findHighestSharedBlockHash(peerSelectedTipHash *externalapi.DomainHash) (lowHash *externalapi.DomainHash,
@@ -123,17 +268,17 @@ func (flow *handleIBDFlow) receiveBlockLocator() (blockLocatorHashes []*external
return msgBlockLocator.BlockLocatorHashes, nil
}
func (flow *handleIBDFlow) downloadBlocks(highestSharedBlockHash *externalapi.DomainHash,
func (flow *handleIBDFlow) downloadHeaders(highestSharedBlockHash *externalapi.DomainHash,
peerSelectedTipHash *externalapi.DomainHash) error {
err := flow.sendGetBlocks(highestSharedBlockHash, peerSelectedTipHash)
err := flow.sendRequestHeaders(highestSharedBlockHash, peerSelectedTipHash)
if err != nil {
return err
}
blocksReceived := 0
for {
msgIBDBlock, doneIBD, err := flow.receiveIBDBlock()
msgBlockHeader, doneIBD, err := flow.receiveHeader()
if err != nil {
return err
}
@@ -142,14 +287,14 @@ func (flow *handleIBDFlow) downloadBlocks(highestSharedBlockHash *externalapi.Do
return nil
}
err = flow.processIBDBlock(msgIBDBlock)
err = flow.processHeader(msgBlockHeader)
if err != nil {
return err
}
blocksReceived++
if blocksReceived%ibdBatchSize == 0 {
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestNextIBDBlocks())
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestNextHeaders())
if err != nil {
return err
}
@@ -157,57 +302,54 @@ func (flow *handleIBDFlow) downloadBlocks(highestSharedBlockHash *externalapi.Do
}
}
func (flow *handleIBDFlow) sendGetBlocks(highestSharedBlockHash *externalapi.DomainHash,
func (flow *handleIBDFlow) sendRequestHeaders(highestSharedBlockHash *externalapi.DomainHash,
peerSelectedTipHash *externalapi.DomainHash) error {
msgGetBlockInvs := appmessage.NewMsgRequstIBDBlocks(highestSharedBlockHash, peerSelectedTipHash)
msgGetBlockInvs := appmessage.NewMsgRequstHeaders(highestSharedBlockHash, peerSelectedTipHash)
return flow.outgoingRoute.Enqueue(msgGetBlockInvs)
}
func (flow *handleIBDFlow) receiveIBDBlock() (msgIBDBlock *appmessage.MsgIBDBlock, doneIBD bool, err error) {
func (flow *handleIBDFlow) receiveHeader() (msgIBDBlock *appmessage.MsgBlockHeader, doneIBD bool, err error) {
message, err := flow.incomingRoute.DequeueWithTimeout(common.DefaultTimeout)
if err != nil {
return nil, false, err
}
switch message := message.(type) {
case *appmessage.MsgIBDBlock:
case *appmessage.MsgBlockHeader:
return message, false, nil
case *appmessage.MsgDoneIBDBlocks:
case *appmessage.MsgDoneHeaders:
return nil, true, nil
default:
return nil, false,
protocolerrors.Errorf(true, "received unexpected message type. "+
"expected: %s, got: %s", appmessage.CmdIBDBlock, message.Command())
"expected: %s or %s, got: %s", appmessage.CmdHeader, appmessage.CmdDoneHeaders, message.Command())
}
}
func (flow *handleIBDFlow) processIBDBlock(msgIBDBlock *appmessage.MsgIBDBlock) error {
block := appmessage.MsgBlockToDomainBlock(msgIBDBlock.MsgBlock)
func (flow *handleIBDFlow) processHeader(msgBlockHeader *appmessage.MsgBlockHeader) error {
header := appmessage.BlockHeaderToDomainBlockHeader(msgBlockHeader)
block := &externalapi.DomainBlock{
Header: header,
Transactions: nil,
}
blockHash := consensusserialization.BlockHash(block)
blockInfo, err := flow.Domain().Consensus().GetBlockInfo(blockHash)
if err != nil {
return err
}
if blockInfo.Exists {
log.Debugf("IBD block %s is already in the DAG. Skipping...", blockHash)
log.Debugf("Block header %s is already in the DAG. Skipping...", blockHash)
return nil
}
err = flow.Domain().Consensus().ValidateAndInsertBlock(block)
if err != nil {
if !errors.As(err, &ruleerrors.RuleError{}) {
return errors.Wrapf(err, "failed to process block %s during IBD", blockHash)
return errors.Wrapf(err, "failed to process header %s during IBD", blockHash)
}
log.Infof("Rejected block %s from %s during IBD: %s", blockHash, flow.peer, err)
log.Infof("Rejected block header %s from %s during IBD: %s", blockHash, flow.peer, err)
return protocolerrors.Wrapf(true, err, "got invalid block %s during IBD", blockHash)
}
err = flow.OnNewBlock(block)
if err != nil {
return err
}
err = blocklogger.LogBlock(block)
if err != nil {
return err
}
return nil
}

View File

@@ -14,6 +14,10 @@ import (
"github.com/kaspanet/kaspad/util/mstime"
)
// maxProtocolVersion version is the maximum supported protocol
// version this kaspad node supports
const maxProtocolVersion = 1
// Peer holds data about a peer.
type Peer struct {
connection *netadapter.NetConnection
@@ -112,9 +116,9 @@ func (p *Peer) IsOutbound() bool {
func (p *Peer) UpdateFieldsFromMsgVersion(msg *appmessage.MsgVersion) {
// Negotiate the protocol version.
p.advertisedProtocolVerion = msg.ProtocolVersion
p.protocolVersion = mathUtil.MinUint32(p.protocolVersion, p.advertisedProtocolVerion)
p.protocolVersion = mathUtil.MinUint32(maxProtocolVersion, p.advertisedProtocolVerion)
log.Debugf("Negotiated protocol version %d for peer %s",
p.protocolVersion, p.ID())
p.protocolVersion, p)
// Set the supported services for the peer to what the remote peer
// advertised.

View File

@@ -50,15 +50,9 @@ func (m *Manager) routerInitializer(router *routerpkg.Router, netConnection *net
return
}
spawn("Manager.routerInitializer-netConnection.DequeueInvalidMessage", func() {
for {
isOpen, err := netConnection.DequeueInvalidMessage()
if !isOpen {
return
}
if atomic.AddUint32(&isStopping, 1) == 1 {
errChan <- protocolerrors.Wrap(true, err, "received bad message")
}
netConnection.SetOnInvalidMessageHandler(func(err error) {
if atomic.AddUint32(&isStopping, 1) == 1 {
errChan <- protocolerrors.Wrap(true, err, "received bad message")
}
})
@@ -124,7 +118,7 @@ func (m *Manager) registerAddressFlows(router *routerpkg.Router, isStopping *uin
outgoingRoute := router.OutgoingRoute()
return []*flow{
m.registerOneTimeFlow("SendAddresses", router, []appmessage.MessageCommand{appmessage.CmdRequestAddresses}, isStopping, errChan,
m.registerFlow("SendAddresses", router, []appmessage.MessageCommand{appmessage.CmdRequestAddresses}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return addressexchange.SendAddresses(m.context, incomingRoute, outgoingRoute)
},
@@ -180,33 +174,52 @@ func (m *Manager) registerIBDFlows(router *routerpkg.Router, isStopping *uint32,
return []*flow{
m.registerFlow("HandleIBD", router, []appmessage.MessageCommand{appmessage.CmdBlockLocator, appmessage.CmdIBDBlock,
appmessage.CmdDoneIBDBlocks}, isStopping, errChan,
appmessage.CmdDoneHeaders, appmessage.CmdIBDRootNotFound, appmessage.CmdIBDRootUTXOSetAndBlock, appmessage.CmdHeader},
isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return ibd.HandleIBD(m.context, incomingRoute, outgoingRoute, peer)
},
),
m.registerFlow("RequestSelectedTip", router, []appmessage.MessageCommand{appmessage.CmdSelectedTip}, isStopping, errChan,
m.registerFlow("RequestSelectedTip", router,
[]appmessage.MessageCommand{appmessage.CmdSelectedTip}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return selectedtip.RequestSelectedTip(m.context, incomingRoute, outgoingRoute, peer)
},
),
m.registerFlow("HandleRequestSelectedTip", router, []appmessage.MessageCommand{appmessage.CmdRequestSelectedTip}, isStopping, errChan,
m.registerFlow("HandleRequestSelectedTip", router,
[]appmessage.MessageCommand{appmessage.CmdRequestSelectedTip}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return selectedtip.HandleRequestSelectedTip(m.context, incomingRoute, outgoingRoute)
},
),
m.registerFlow("HandleRequestBlockLocator", router, []appmessage.MessageCommand{appmessage.CmdRequestBlockLocator}, isStopping, errChan,
m.registerFlow("HandleRequestBlockLocator", router,
[]appmessage.MessageCommand{appmessage.CmdRequestBlockLocator}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return ibd.HandleRequestBlockLocator(m.context, incomingRoute, outgoingRoute)
},
),
m.registerFlow("HandleRequestIBDBlocks", router, []appmessage.MessageCommand{appmessage.CmdRequestIBDBlocks, appmessage.CmdRequestNextIBDBlocks}, isStopping, errChan,
m.registerFlow("HandleRequestHeaders", router,
[]appmessage.MessageCommand{appmessage.CmdRequestHeaders, appmessage.CmdRequestNextHeaders}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return ibd.HandleRequestIBDBlocks(m.context, incomingRoute, outgoingRoute)
return ibd.HandleRequestHeaders(m.context, incomingRoute, outgoingRoute)
},
),
m.registerFlow("HandleRequestIBDRootUTXOSetAndBlock", router,
[]appmessage.MessageCommand{appmessage.CmdRequestIBDRootUTXOSetAndBlock}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return ibd.HandleRequestIBDRootUTXOSetAndBlock(m.context, incomingRoute, outgoingRoute)
},
),
m.registerFlow("HandleIBDBlockRequests", router,
[]appmessage.MessageCommand{appmessage.CmdRequestIBDBlocks}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return ibd.HandleIBDBlockRequests(m.context, incomingRoute, outgoingRoute)
},
),
}

View File

@@ -1,6 +1,9 @@
package protocolerrors
import "github.com/pkg/errors"
import (
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/pkg/errors"
)
// ProtocolError is an error that signifies a violation
// of the peer-to-peer protocol
@@ -50,3 +53,14 @@ func Wrapf(shouldBan bool, err error, format string, args ...interface{}) error
Cause: errors.Wrapf(err, format, args...),
}
}
// ConvertToBanningProtocolErrorIfRuleError converts the given error to
// a banning protocol error if it's a rule error, and otherwise keep it
// as is.
func ConvertToBanningProtocolErrorIfRuleError(err error, format string, args ...interface{}) error {
if !errors.As(err, &ruleerrors.RuleError{}) {
return err
}
return Wrapf(true, err, format, args...)
}

View File

@@ -32,7 +32,7 @@ var handlers = map[appmessage.MessageCommand]handler{
appmessage.CmdResolveFinalityConflictRequestMessage: rpchandlers.HandleResolveFinalityConflict,
appmessage.CmdNotifyFinalityConflictsRequestMessage: rpchandlers.HandleNotifyFinalityConflicts,
appmessage.CmdGetMempoolEntriesRequestMessage: rpchandlers.HandleGetMempoolEntries,
appmessage.CmdShutDownRequestMessage: rpchandlers.HandleGetMempoolEntries,
appmessage.CmdShutDownRequestMessage: rpchandlers.HandleShutDown,
appmessage.CmdGetHeadersRequestMessage: rpchandlers.HandleGetHeaders,
}

View File

@@ -3,6 +3,7 @@ package rpccontext
import (
"encoding/hex"
"fmt"
"github.com/kaspanet/kaspad/domain/consensus/utils/blocks"
"math/big"
"strconv"
@@ -25,6 +26,11 @@ func (ctx *Context) BuildBlockVerboseData(block *externalapi.DomainBlock, includ
hash := consensusserialization.BlockHash(block)
blockHeader := block.Header
blueScore, err := blocks.ExtractBlueScore(block)
if err != nil {
return nil, err
}
result := &appmessage.BlockVerboseData{
Hash: hash.String(),
Version: blockHeader.Version,
@@ -37,6 +43,7 @@ func (ctx *Context) BuildBlockVerboseData(block *externalapi.DomainBlock, includ
Time: blockHeader.TimeInMilliseconds,
Bits: strconv.FormatInt(int64(blockHeader.Bits), 16),
Difficulty: ctx.GetDifficultyRatio(blockHeader.Bits, ctx.Config.ActiveNetParams),
BlueScore: blueScore,
}
txIDs := make([]string, len(block.Transactions))

View File

@@ -8,6 +8,10 @@ import (
// HandleGetBlockCount handles the respectively named RPC command
func HandleGetBlockCount(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
response := appmessage.NewGetBlockCountResponseMessage(0) // TODO
syncInfo, err := context.Domain.Consensus().GetSyncInfo()
if err != nil {
return nil, err
}
response := appmessage.NewGetBlockCountResponseMessage(syncInfo.BlockCount)
return response, nil
}

View File

@@ -27,7 +27,11 @@ func HandleGetBlockTemplate(context *rpccontext.Context, _ *router.Router, reque
coinbaseData := &externalapi.DomainCoinbaseData{ScriptPublicKey: scriptPublicKey}
templateBlock := context.Domain.MiningManager().GetBlockTemplate(coinbaseData)
templateBlock, err := context.Domain.MiningManager().GetBlockTemplate(coinbaseData)
if err != nil {
return nil, err
}
msgBlock := appmessage.DomainBlockToMsgBlock(templateBlock)
return appmessage.DomainBlockToMsgBlock(templateBlock), nil
return appmessage.NewGetBlockTemplateResponseMessage(msgBlock), nil
}

View File

@@ -14,6 +14,7 @@ const (
// HandleGetBlocks handles the respectively named RPC command
func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
return nil, nil
response := &appmessage.GetBlocksResponseMessage{}
response.Error = appmessage.RPCErrorf("not implemented")
return response, nil
}

View File

@@ -14,5 +14,7 @@ const (
// HandleGetChainFromBlock handles the respectively named RPC command
func HandleGetChainFromBlock(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
return nil, nil
response := &appmessage.GetChainFromBlockResponseMessage{}
response.Error = appmessage.RPCErrorf("not implemented")
return response, nil
}

View File

@@ -8,5 +8,7 @@ import (
// HandleGetHeaders handles the respectively named RPC command
func HandleGetHeaders(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
return nil, nil
response := &appmessage.GetHeadersResponseMessage{}
response.Error = appmessage.RPCErrorf("not implemented")
return response, nil
}

View File

@@ -8,5 +8,7 @@ import (
// HandleGetMempoolEntries handles the respectively named RPC command
func HandleGetMempoolEntries(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
return nil, nil
response := &appmessage.GetMempoolEntriesResponseMessage{}
response.Error = appmessage.RPCErrorf("not implemented")
return response, nil
}

View File

@@ -8,5 +8,5 @@ import (
// HandleGetMempoolEntry handles the respectively named RPC command
func HandleGetMempoolEntry(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
return nil, nil
return &appmessage.GetMempoolEntryResponseMessage{}, nil
}

View File

@@ -1,6 +1,9 @@
package rpchandlers
import (
"net"
"strconv"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
@@ -8,14 +11,20 @@ import (
// HandleGetPeerAddresses handles the respectively named RPC command
func HandleGetPeerAddresses(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
peersState, err := context.AddressManager.PeersStateForSerialization()
if err != nil {
return nil, err
netAddresses := context.AddressManager.Addresses()
addressMessages := make([]*appmessage.GetPeerAddressesKnownAddressMessage, len(netAddresses))
for i, netAddress := range netAddresses {
addressWithPort := net.JoinHostPort(netAddress.IP.String(), strconv.FormatUint(uint64(netAddress.Port), 10))
addressMessages[i] = &appmessage.GetPeerAddressesKnownAddressMessage{Addr: addressWithPort}
}
addresses := make([]*appmessage.GetPeerAddressesKnownAddressMessage, len(peersState.Addresses))
for i, address := range peersState.Addresses {
addresses[i] = &appmessage.GetPeerAddressesKnownAddressMessage{Addr: string(address.Address)}
bannedAddresses := context.AddressManager.BannedAddresses()
bannedAddressMessages := make([]*appmessage.GetPeerAddressesKnownAddressMessage, len(bannedAddresses))
for i, netAddress := range bannedAddresses {
addressWithPort := net.JoinHostPort(netAddress.IP.String(), strconv.FormatUint(uint64(netAddress.Port), 10))
bannedAddressMessages[i] = &appmessage.GetPeerAddressesKnownAddressMessage{Addr: addressWithPort}
}
response := appmessage.NewGetPeerAddressesResponseMessage(addresses)
response := appmessage.NewGetPeerAddressesResponseMessage(addressMessages, bannedAddressMessages)
return response, nil
}

View File

@@ -8,5 +8,7 @@ import (
// HandleGetSubnetwork handles the respectively named RPC command
func HandleGetSubnetwork(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
return nil, nil
response := &appmessage.GetSubnetworkResponseMessage{}
response.Error = appmessage.RPCErrorf("not implemented")
return response, nil
}

View File

@@ -8,5 +8,7 @@ import (
// HandleResolveFinalityConflict handles the respectively named RPC command
func HandleResolveFinalityConflict(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
return nil, nil
response := &appmessage.ResolveFinalityConflictResponseMessage{}
response.Error = appmessage.RPCErrorf("not implemented")
return response, nil
}

View File

@@ -21,7 +21,7 @@ func HandleSubmitBlock(context *rpccontext.Context, _ *router.Router, request ap
return errorMessage, nil
}
log.Infof("Accepted domainBlock %s via submitBlock", consensusserialization.BlockHash(domainBlock))
log.Infof("Accepted block %s via submitBlock", consensusserialization.BlockHash(domainBlock))
response := appmessage.NewSubmitBlockResponseMessage()
return response, nil

View File

@@ -6,7 +6,8 @@ RUN mkdir -p /go/src/github.com/kaspanet/kaspad
WORKDIR /go/src/github.com/kaspanet/kaspad
RUN apk add --no-cache curl git openssh binutils gcc musl-dev
RUN go get -u golang.org/x/lint/golint
RUN go get -u golang.org/x/lint/golint \
honnef.co/go/tools/cmd/staticcheck
COPY go.mod .
COPY go.sum .
@@ -20,6 +21,7 @@ WORKDIR /go/src/github.com/kaspanet/kaspad/cmd/kaspactl
RUN GOFMT_RESULT=`go fmt ./...`; echo $GOFMT_RESULT; test -z "$GOFMT_RESULT"
RUN go vet ./...
RUN golint -set_exit_status ./...
RUN staticcheck -checks SA4006 ./...
RUN GOOS=linux go build -a -installsuffix cgo -o kaspactl .
# --- multistage docker build: stage #2: runtime image

View File

@@ -6,7 +6,8 @@ RUN mkdir -p /go/src/github.com/kaspanet/kaspad
WORKDIR /go/src/github.com/kaspanet/kaspad
RUN apk add --no-cache curl git openssh binutils gcc musl-dev
RUN go get -u golang.org/x/lint/golint
RUN go get -u golang.org/x/lint/golint \
honnef.co/go/tools/cmd/staticcheck
COPY go.mod .
COPY go.sum .
@@ -20,6 +21,7 @@ WORKDIR /go/src/github.com/kaspanet/kaspad/cmd/kaspaminer
RUN GOFMT_RESULT=`go fmt ./...`; echo $GOFMT_RESULT; test -z "$GOFMT_RESULT"
RUN go vet ./...
RUN golint -set_exit_status ./...
RUN staticcheck -checks SA4006 ./...
RUN GOOS=linux go build -a -installsuffix cgo -o kaspaminer .
# --- multistage docker build: stage #2: runtime image

View File

@@ -10,7 +10,8 @@ RUN go get -u golang.org/x/lint/golint \
github.com/kisielk/errcheck \
github.com/opennota/check/cmd/aligncheck \
github.com/opennota/check/cmd/structcheck \
github.com/opennota/check/cmd/varcheck
github.com/opennota/check/cmd/varcheck \
honnef.co/go/tools/cmd/staticcheck
COPY go.mod .
COPY go.sum .
@@ -25,6 +26,7 @@ RUN golint -set_exit_status ./...
# RUN aligncheck ./...
# RUN structcheck -e ./...
# RUN varcheck -e ./...
RUN staticcheck -checks "SA4006,SA4008,SA4009,SA4010,SA5003,SA1004,SA1014,SA1021,SA1023,SA1024,SA1025,SA1026,SA1027,SA1028,SA2000,SA2001,SA2003,SA4000,SA4001,SA4003,SA4004,SA4011,SA4012,SA4013,SA4014,SA4015,SA4016,SA4017,SA4018,SA4019,SA4020,SA4021,SA4022,SA4023,SA5000,SA5002,SA5004,SA5005,SA5007,SA5008,SA5009,SA5010,SA5011,SA5012,SA6001,SA6002,SA9001,SA9002,SA9003,SA9004,SA9005,SA9006,ST1019" ./...
RUN GOOS=linux go build -a -installsuffix cgo -o kaspad .
# Remove the line below and uncomment the line after it for testing with coverage

View File

@@ -1,44 +1,47 @@
package consensus
import (
"sync"
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/pkg/errors"
)
// Consensus maintains the current core state of the node
type Consensus interface {
BuildBlock(coinbaseData *externalapi.DomainCoinbaseData, transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlock, error)
ValidateAndInsertBlock(block *externalapi.DomainBlock) error
ValidateTransactionAndPopulateWithConsensusData(transaction *externalapi.DomainTransaction) error
GetBlock(blockHash *externalapi.DomainHash) (*externalapi.DomainBlock, error)
GetBlockHeader(blockHash *externalapi.DomainHash) (*externalapi.DomainBlockHeader, error)
GetBlockInfo(blockHash *externalapi.DomainHash) (*externalapi.BlockInfo, error)
GetHashesBetween(lowHash, highHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error)
GetMissingBlockBodyHashes(highHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error)
GetPruningPointUTXOSet() ([]byte, error)
SetPruningPointUTXOSet(serializedUTXOSet []byte) error
GetVirtualSelectedParent() (*externalapi.DomainBlock, error)
CreateBlockLocator(lowHash, highHash *externalapi.DomainHash) (externalapi.BlockLocator, error)
FindNextBlockLocatorBoundaries(blockLocator externalapi.BlockLocator) (lowHash, highHash *externalapi.DomainHash, err error)
GetSyncInfo() (*externalapi.SyncInfo, error)
}
type consensus struct {
lock *sync.Mutex
databaseContext model.DBReader
blockProcessor model.BlockProcessor
blockBuilder model.BlockBuilder
consensusStateManager model.ConsensusStateManager
transactionValidator model.TransactionValidator
syncManager model.SyncManager
pastMedianTimeManager model.PastMedianTimeManager
blockValidator model.BlockValidator
coinbaseManager model.CoinbaseManager
dagTopologyManager model.DAGTopologyManager
dagTraversalManager model.DAGTraversalManager
difficultyManager model.DifficultyManager
ghostdagManager model.GHOSTDAGManager
headerTipsManager model.HeaderTipsManager
mergeDepthManager model.MergeDepthManager
pruningManager model.PruningManager
reachabilityManager model.ReachabilityManager
blockStore model.BlockStore
blockHeaderStore model.BlockHeaderStore
pruningStore model.PruningStore
ghostdagDataStore model.GHOSTDAGDataStore
blockStatusStore model.BlockStatusStore
acceptanceDataStore model.AcceptanceDataStore
blockStore model.BlockStore
blockHeaderStore model.BlockHeaderStore
pruningStore model.PruningStore
ghostdagDataStore model.GHOSTDAGDataStore
blockRelationStore model.BlockRelationStore
blockStatusStore model.BlockStatusStore
consensusStateStore model.ConsensusStateStore
headerTipsStore model.HeaderTipsStore
multisetStore model.MultisetStore
reachabilityDataStore model.ReachabilityDataStore
utxoDiffStore model.UTXODiffStore
}
// BuildBlock builds a block over the current state, with the transactions
@@ -46,18 +49,27 @@ type consensus struct {
func (s *consensus) BuildBlock(coinbaseData *externalapi.DomainCoinbaseData,
transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlock, error) {
return s.blockProcessor.BuildBlock(coinbaseData, transactions)
s.lock.Lock()
defer s.lock.Unlock()
return s.blockBuilder.BuildBlock(coinbaseData, transactions)
}
// ValidateAndInsertBlock validates the given block and, if valid, applies it
// to the current state
func (s *consensus) ValidateAndInsertBlock(block *externalapi.DomainBlock) error {
s.lock.Lock()
defer s.lock.Unlock()
return s.blockProcessor.ValidateAndInsertBlock(block)
}
// ValidateTransactionAndPopulateWithConsensusData validates the given transaction
// and populates it with any missing consensus data
func (s *consensus) ValidateTransactionAndPopulateWithConsensusData(transaction *externalapi.DomainTransaction) error {
s.lock.Lock()
defer s.lock.Unlock()
err := s.transactionValidator.ValidateTransactionInIsolation(transaction)
if err != nil {
return err
@@ -68,11 +80,7 @@ func (s *consensus) ValidateTransactionAndPopulateWithConsensusData(transaction
return err
}
virtualGHOSTDAGData, err := s.ghostdagDataStore.Get(s.databaseContext, model.VirtualBlockHash)
if err != nil {
return err
}
virtualSelectedParentMedianTime, err := s.pastMedianTimeManager.PastMedianTime(virtualGHOSTDAGData.SelectedParent)
virtualSelectedParentMedianTime, err := s.pastMedianTimeManager.PastMedianTime(model.VirtualBlockHash)
if err != nil {
return err
}
@@ -82,14 +90,23 @@ func (s *consensus) ValidateTransactionAndPopulateWithConsensusData(transaction
}
func (s *consensus) GetBlock(blockHash *externalapi.DomainHash) (*externalapi.DomainBlock, error) {
s.lock.Lock()
defer s.lock.Unlock()
return s.blockStore.Block(s.databaseContext, blockHash)
}
func (s *consensus) GetBlockHeader(blockHash *externalapi.DomainHash) (*externalapi.DomainBlockHeader, error) {
s.lock.Lock()
defer s.lock.Unlock()
return s.blockHeaderStore.BlockHeader(s.databaseContext, blockHash)
}
func (s *consensus) GetBlockInfo(blockHash *externalapi.DomainHash) (*externalapi.BlockInfo, error) {
s.lock.Lock()
defer s.lock.Unlock()
blockInfo := &externalapi.BlockInfo{}
exists, err := s.blockStatusStore.Exists(s.databaseContext, blockHash)
@@ -107,6 +124,11 @@ func (s *consensus) GetBlockInfo(blockHash *externalapi.DomainHash) (*externalap
}
blockInfo.BlockStatus = blockStatus
// If the status is invalid, then we don't have the necessary reachability data to check if it's in PruningPoint.Future.
if blockStatus == externalapi.StatusInvalid {
return blockInfo, nil
}
isBlockInHeaderPruningPointFuture, err := s.syncManager.IsBlockInHeaderPruningPointFuture(blockHash)
if err != nil {
return nil, err
@@ -117,37 +139,76 @@ func (s *consensus) GetBlockInfo(blockHash *externalapi.DomainHash) (*externalap
}
func (s *consensus) GetHashesBetween(lowHash, highHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
s.lock.Lock()
defer s.lock.Unlock()
return s.syncManager.GetHashesBetween(lowHash, highHash)
}
func (s *consensus) GetMissingBlockBodyHashes(highHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
s.lock.Lock()
defer s.lock.Unlock()
return s.syncManager.GetMissingBlockBodyHashes(highHash)
}
func (s *consensus) GetPruningPointUTXOSet() ([]byte, error) {
return s.pruningStore.PruningPointSerializedUTXOSet(s.databaseContext)
func (s *consensus) GetPruningPointUTXOSet(expectedPruningPointHash *externalapi.DomainHash) ([]byte, error) {
s.lock.Lock()
defer s.lock.Unlock()
pruningPointHash, err := s.pruningStore.PruningPoint(s.databaseContext)
if err != nil {
return nil, err
}
if *expectedPruningPointHash != *pruningPointHash {
return nil, errors.Wrapf(ruleerrors.ErrWrongPruningPointHash, "expected pruning point %s but got %s",
expectedPruningPointHash,
pruningPointHash)
}
serializedUTXOSet, err := s.pruningStore.PruningPointSerializedUTXOSet(s.databaseContext)
if err != nil {
return nil, err
}
return serializedUTXOSet, nil
}
func (s *consensus) SetPruningPointUTXOSet(serializedUTXOSet []byte) error {
s.lock.Lock()
defer s.lock.Unlock()
return s.consensusStateManager.SetPruningPointUTXOSet(serializedUTXOSet)
}
func (s *consensus) GetVirtualSelectedParent() (*externalapi.DomainBlock, error) {
s.lock.Lock()
defer s.lock.Unlock()
virtualGHOSTDAGData, err := s.ghostdagDataStore.Get(s.databaseContext, model.VirtualBlockHash)
if err != nil {
return nil, err
}
return s.GetBlock(virtualGHOSTDAGData.SelectedParent)
return s.blockStore.Block(s.databaseContext, virtualGHOSTDAGData.SelectedParent)
}
func (s *consensus) CreateBlockLocator(lowHash, highHash *externalapi.DomainHash) (externalapi.BlockLocator, error) {
s.lock.Lock()
defer s.lock.Unlock()
return s.syncManager.CreateBlockLocator(lowHash, highHash)
}
func (s *consensus) FindNextBlockLocatorBoundaries(blockLocator externalapi.BlockLocator) (lowHash, highHash *externalapi.DomainHash, err error) {
s.lock.Lock()
defer s.lock.Unlock()
return s.syncManager.FindNextBlockLocatorBoundaries(blockLocator)
}
func (s *consensus) GetSyncInfo() (*externalapi.SyncInfo, error) {
s.lock.Lock()
defer s.lock.Unlock()
return s.syncManager.GetSyncInfo()
}

View File

@@ -0,0 +1,76 @@
package consensus
import (
"testing"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/pkg/errors"
)
func TestConsensus_GetBlockInfo(t *testing.T) {
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
factory := NewFactory()
consensus, teardown, err := factory.NewTestConsensus(params, "TestConsensus_GetBlockInfo")
if err != nil {
t.Fatalf("Error setting up consensus: %+v", err)
}
defer teardown()
invalidBlock, _, err := consensus.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
if err != nil {
t.Fatal(err)
}
invalidBlock.Header.TimeInMilliseconds = 0
err = consensus.ValidateAndInsertBlock(invalidBlock)
if !errors.Is(err, ruleerrors.ErrTimeTooOld) {
t.Fatalf("Expected block to be invalid with err: %v, instead found: %v", ruleerrors.ErrTimeTooOld, err)
}
info, err := consensus.GetBlockInfo(consensusserialization.BlockHash(invalidBlock))
if err != nil {
t.Fatalf("Failed to get block info: %v", err)
}
if !info.Exists {
t.Fatal("The block is missing")
}
if info.BlockStatus != externalapi.StatusInvalid {
t.Fatalf("Expected block status: %s, instead got: %s", externalapi.StatusInvalid, info.BlockStatus)
}
if info.IsBlockInHeaderPruningPointFuture != false {
t.Fatalf("Expected IsBlockInHeaderPruningPointFuture=false, instead found: %t", info.IsBlockInHeaderPruningPointFuture)
}
emptyCoinbase := externalapi.DomainCoinbaseData{}
validBlock, err := consensus.BuildBlock(&emptyCoinbase, nil)
if err != nil {
t.Fatalf("consensus.BuildBlock with an empty coinbase shouldn't fail: %v", err)
}
err = consensus.ValidateAndInsertBlock(validBlock)
if err != nil {
t.Fatalf("consensus.ValidateAndInsertBlock with a block straight from consensus.BuildBlock should not fail: %v", err)
}
info, err = consensus.GetBlockInfo(consensusserialization.BlockHash(validBlock))
if err != nil {
t.Fatalf("Failed to get block info: %v", err)
}
if !info.Exists {
t.Fatal("The block is missing")
}
if info.BlockStatus != externalapi.StatusValid {
t.Fatalf("Expected block status: %s, instead got: %s", externalapi.StatusValid, info.BlockStatus)
}
if info.IsBlockInHeaderPruningPointFuture != true {
t.Fatalf("Expected IsBlockInHeaderPruningPointFuture=true, instead found: %t", info.IsBlockInHeaderPruningPointFuture)
}
})
}

View File

@@ -10,8 +10,9 @@ func bluesAnticoneSizesToDBBluesAnticoneSizes(bluesAnticoneSizes map[externalapi
dbBluesAnticoneSizes := make([]*DbBluesAnticoneSizes, len(bluesAnticoneSizes))
i := 0
for hash, anticoneSize := range bluesAnticoneSizes {
hashCopy := hash
dbBluesAnticoneSizes[i] = &DbBluesAnticoneSizes{
BlueHash: DomainHashToDbHash(&hash),
BlueHash: DomainHashToDbHash(&hashCopy),
AnticoneSize: uint32(anticoneSize),
}
i++

View File

@@ -0,0 +1,27 @@
package serialization
import (
"github.com/kaspanet/kaspad/domain/consensus/model"
"testing"
)
// TestBlueAnticoneSizesSize tests that no data can be loss when converting model.KType to the corresponding type in
// DbBluesAnticoneSizes
func TestKType(t *testing.T) {
k := model.KType(0)
k--
if k < model.KType(0) {
t.Fatalf("KType must be unsigned")
}
// Setting maxKType to maximum value of KType.
// As we verify above that KType is unsigned we can be sure that maxKType is indeed the maximum value of KType.
maxKType := ^model.KType(0)
dbBluesAnticoneSizes := DbBluesAnticoneSizes{
AnticoneSize: uint32(maxKType),
}
if model.KType(dbBluesAnticoneSizes.AnticoneSize) != maxKType {
t.Fatalf("convert from uint32 to KType losses data")
}
}

View File

@@ -1535,6 +1535,194 @@ func (x *DbHeaderTips) GetTips() []*DbHash {
return nil
}
type DbTips struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Tips []*DbHash `protobuf:"bytes,1,rep,name=tips,proto3" json:"tips,omitempty"`
}
func (x *DbTips) Reset() {
*x = DbTips{}
if protoimpl.UnsafeEnabled {
mi := &file_dbobjects_proto_msgTypes[26]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DbTips) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DbTips) ProtoMessage() {}
func (x *DbTips) ProtoReflect() protoreflect.Message {
mi := &file_dbobjects_proto_msgTypes[26]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DbTips.ProtoReflect.Descriptor instead.
func (*DbTips) Descriptor() ([]byte, []int) {
return file_dbobjects_proto_rawDescGZIP(), []int{26}
}
func (x *DbTips) GetTips() []*DbHash {
if x != nil {
return x.Tips
}
return nil
}
type DbVirtualDiffParents struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
VirtualDiffParents []*DbHash `protobuf:"bytes,1,rep,name=virtualDiffParents,proto3" json:"virtualDiffParents,omitempty"`
}
func (x *DbVirtualDiffParents) Reset() {
*x = DbVirtualDiffParents{}
if protoimpl.UnsafeEnabled {
mi := &file_dbobjects_proto_msgTypes[27]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DbVirtualDiffParents) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DbVirtualDiffParents) ProtoMessage() {}
func (x *DbVirtualDiffParents) ProtoReflect() protoreflect.Message {
mi := &file_dbobjects_proto_msgTypes[27]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DbVirtualDiffParents.ProtoReflect.Descriptor instead.
func (*DbVirtualDiffParents) Descriptor() ([]byte, []int) {
return file_dbobjects_proto_rawDescGZIP(), []int{27}
}
func (x *DbVirtualDiffParents) GetVirtualDiffParents() []*DbHash {
if x != nil {
return x.VirtualDiffParents
}
return nil
}
type DbBlockCount struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Count uint64 `protobuf:"varint,1,opt,name=count,proto3" json:"count,omitempty"`
}
func (x *DbBlockCount) Reset() {
*x = DbBlockCount{}
if protoimpl.UnsafeEnabled {
mi := &file_dbobjects_proto_msgTypes[28]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DbBlockCount) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DbBlockCount) ProtoMessage() {}
func (x *DbBlockCount) ProtoReflect() protoreflect.Message {
mi := &file_dbobjects_proto_msgTypes[28]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DbBlockCount.ProtoReflect.Descriptor instead.
func (*DbBlockCount) Descriptor() ([]byte, []int) {
return file_dbobjects_proto_rawDescGZIP(), []int{28}
}
func (x *DbBlockCount) GetCount() uint64 {
if x != nil {
return x.Count
}
return 0
}
type DbBlockHeaderCount struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Count uint64 `protobuf:"varint,1,opt,name=count,proto3" json:"count,omitempty"`
}
func (x *DbBlockHeaderCount) Reset() {
*x = DbBlockHeaderCount{}
if protoimpl.UnsafeEnabled {
mi := &file_dbobjects_proto_msgTypes[29]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DbBlockHeaderCount) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DbBlockHeaderCount) ProtoMessage() {}
func (x *DbBlockHeaderCount) ProtoReflect() protoreflect.Message {
mi := &file_dbobjects_proto_msgTypes[29]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DbBlockHeaderCount.ProtoReflect.Descriptor instead.
func (*DbBlockHeaderCount) Descriptor() ([]byte, []int) {
return file_dbobjects_proto_rawDescGZIP(), []int{29}
}
func (x *DbBlockHeaderCount) GetCount() uint64 {
if x != nil {
return x.Count
}
return 0
}
var File_dbobjects_proto protoreflect.FileDescriptor
var file_dbobjects_proto_rawDesc = []byte{
@@ -1754,11 +1942,25 @@ var file_dbobjects_proto_rawDesc = []byte{
0x74, 0x65, 0x73, 0x22, 0x39, 0x0a, 0x0c, 0x44, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x54,
0x69, 0x70, 0x73, 0x12, 0x29, 0x0a, 0x04, 0x74, 0x69, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x15, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x2e, 0x44, 0x62, 0x48, 0x61, 0x73, 0x68, 0x52, 0x04, 0x74, 0x69, 0x70, 0x73, 0x42, 0x2a,
0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x61, 0x73,
0x70, 0x61, 0x6e, 0x65, 0x74, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x64, 0x2f, 0x73, 0x65, 0x72,
0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
0x6e, 0x2e, 0x44, 0x62, 0x48, 0x61, 0x73, 0x68, 0x52, 0x04, 0x74, 0x69, 0x70, 0x73, 0x22, 0x33,
0x0a, 0x06, 0x44, 0x62, 0x54, 0x69, 0x70, 0x73, 0x12, 0x29, 0x0a, 0x04, 0x74, 0x69, 0x70, 0x73,
0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69,
0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x62, 0x48, 0x61, 0x73, 0x68, 0x52, 0x04, 0x74,
0x69, 0x70, 0x73, 0x22, 0x5d, 0x0a, 0x14, 0x44, 0x62, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c,
0x44, 0x69, 0x66, 0x66, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x45, 0x0a, 0x12, 0x76,
0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x44, 0x69, 0x66, 0x66, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74,
0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c,
0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x62, 0x48, 0x61, 0x73, 0x68, 0x52, 0x12,
0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x44, 0x69, 0x66, 0x66, 0x50, 0x61, 0x72, 0x65, 0x6e,
0x74, 0x73, 0x22, 0x24, 0x0a, 0x0c, 0x44, 0x62, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x75,
0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
0x04, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x2a, 0x0a, 0x12, 0x44, 0x62, 0x42, 0x6c,
0x6f, 0x63, 0x6b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14,
0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63,
0x6f, 0x75, 0x6e, 0x74, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x6e, 0x65, 0x74, 0x2f, 0x6b, 0x61, 0x73, 0x70,
0x61, 0x64, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -1773,7 +1975,7 @@ func file_dbobjects_proto_rawDescGZIP() []byte {
return file_dbobjects_proto_rawDescData
}
var file_dbobjects_proto_msgTypes = make([]protoimpl.MessageInfo, 26)
var file_dbobjects_proto_msgTypes = make([]protoimpl.MessageInfo, 30)
var file_dbobjects_proto_goTypes = []interface{}{
(*DbBlock)(nil), // 0: serialization.DbBlock
(*DbBlockHeader)(nil), // 1: serialization.DbBlockHeader
@@ -1801,6 +2003,10 @@ var file_dbobjects_proto_goTypes = []interface{}{
(*DbUtxoDiff)(nil), // 23: serialization.DbUtxoDiff
(*DbPruningPointUTXOSetBytes)(nil), // 24: serialization.DbPruningPointUTXOSetBytes
(*DbHeaderTips)(nil), // 25: serialization.DbHeaderTips
(*DbTips)(nil), // 26: serialization.DbTips
(*DbVirtualDiffParents)(nil), // 27: serialization.DbVirtualDiffParents
(*DbBlockCount)(nil), // 28: serialization.DbBlockCount
(*DbBlockHeaderCount)(nil), // 29: serialization.DbBlockHeaderCount
}
var file_dbobjects_proto_depIdxs = []int32{
1, // 0: serialization.DbBlock.header:type_name -> serialization.DbBlockHeader
@@ -1836,11 +2042,13 @@ var file_dbobjects_proto_depIdxs = []int32{
18, // 30: serialization.DbUtxoDiff.toAdd:type_name -> serialization.DbUtxoCollectionItem
18, // 31: serialization.DbUtxoDiff.toRemove:type_name -> serialization.DbUtxoCollectionItem
2, // 32: serialization.DbHeaderTips.tips:type_name -> serialization.DbHash
33, // [33:33] is the sub-list for method output_type
33, // [33:33] is the sub-list for method input_type
33, // [33:33] is the sub-list for extension type_name
33, // [33:33] is the sub-list for extension extendee
0, // [0:33] is the sub-list for field type_name
2, // 33: serialization.DbTips.tips:type_name -> serialization.DbHash
2, // 34: serialization.DbVirtualDiffParents.virtualDiffParents:type_name -> serialization.DbHash
35, // [35:35] is the sub-list for method output_type
35, // [35:35] is the sub-list for method input_type
35, // [35:35] is the sub-list for extension type_name
35, // [35:35] is the sub-list for extension extendee
0, // [0:35] is the sub-list for field type_name
}
func init() { file_dbobjects_proto_init() }
@@ -2161,6 +2369,54 @@ func file_dbobjects_proto_init() {
return nil
}
}
file_dbobjects_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DbTips); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_dbobjects_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DbVirtualDiffParents); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_dbobjects_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DbBlockCount); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_dbobjects_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DbBlockHeaderCount); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
@@ -2168,7 +2424,7 @@ func file_dbobjects_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_dbobjects_proto_rawDesc,
NumEnums: 0,
NumMessages: 26,
NumMessages: 30,
NumExtensions: 0,
NumServices: 0,
},

View File

@@ -141,4 +141,20 @@ message DbPruningPointUTXOSetBytes {
message DbHeaderTips {
repeated DbHash tips = 1;
}
}
message DbTips {
repeated DbHash tips = 1;
}
message DbVirtualDiffParents {
repeated DbHash virtualDiffParents = 1;
}
message DbBlockCount {
uint64 count = 1;
}
message DbBlockHeaderCount {
uint64 count = 1;
}

View File

@@ -11,7 +11,7 @@ func HeaderTipsToDBHeaderTips(tips []*externalapi.DomainHash) *DbHeaderTips {
}
}
// DBHeaderTipsTOHeaderTips converts DbHeaderTips to a slice of hashes
func DBHeaderTipsTOHeaderTips(dbHeaderTips *DbHeaderTips) ([]*externalapi.DomainHash, error) {
// DBHeaderTipsToHeaderTips converts DbHeaderTips to a slice of hashes
func DBHeaderTipsToHeaderTips(dbHeaderTips *DbHeaderTips) ([]*externalapi.DomainHash, error) {
return DbHashesToDomainHashes(dbHeaderTips.Tips)
}

View File

@@ -0,0 +1,17 @@
package serialization
import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
)
// TipsToDBTips converts a slice of hashes to DbTips
func TipsToDBTips(tips []*externalapi.DomainHash) *DbTips {
return &DbTips{
Tips: DomainHashesToDbHashes(tips),
}
}
// DBTipsToTips converts DbTips to a slice of hashes
func DBTipsToTips(dbTips *DbTips) ([]*externalapi.DomainHash, error) {
return DbHashesToDomainHashes(dbTips.Tips)
}

View File

@@ -8,8 +8,9 @@ func utxoCollectionToDBUTXOCollection(utxoCollection model.UTXOCollection) []*Db
items := make([]*DbUtxoCollectionItem, len(utxoCollection))
i := 0
for outpoint, entry := range utxoCollection {
outpointCopy := outpoint
items[i] = &DbUtxoCollectionItem{
Outpoint: DomainOutpointToDbOutpoint(&outpoint),
Outpoint: DomainOutpointToDbOutpoint(&outpointCopy),
UtxoEntry: UTXOEntryToDBUTXOEntry(entry),
}
i++

View File

@@ -0,0 +1,17 @@
package serialization
import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
)
// VirtualDiffParentsToDBHeaderVirtualDiffParents converts a slice of hashes to DbVirtualDiffParents
func VirtualDiffParentsToDBHeaderVirtualDiffParents(tips []*externalapi.DomainHash) *DbVirtualDiffParents {
return &DbVirtualDiffParents{
VirtualDiffParents: DomainHashesToDbHashes(tips),
}
}
// DBVirtualDiffParentsToVirtualDiffParents converts DbHeaderTips to a slice of hashes
func DBVirtualDiffParentsToVirtualDiffParents(dbVirtualDiffParents *DbVirtualDiffParents) ([]*externalapi.DomainHash, error) {
return DbHashesToDomainHashes(dbVirtualDiffParents.VirtualDiffParents)
}

View File

@@ -26,7 +26,7 @@ func (d *dbTransaction) Commit() error {
}
func (d *dbTransaction) RollbackUnlessClosed() error {
return d.RollbackUnlessClosed()
return d.transaction.RollbackUnlessClosed()
}
func newDBTransaction(transaction database.Transaction) model.DBTransaction {

View File

@@ -5,6 +5,7 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
"google.golang.org/protobuf/proto"
)
@@ -14,19 +15,21 @@ var bucket = dbkeys.MakeBucket([]byte("acceptance-data"))
type acceptanceDataStore struct {
staging map[externalapi.DomainHash]model.AcceptanceData
toDelete map[externalapi.DomainHash]struct{}
cache *lrucache.LRUCache
}
// New instantiates a new AcceptanceDataStore
func New() model.AcceptanceDataStore {
func New(cacheSize int) model.AcceptanceDataStore {
return &acceptanceDataStore{
staging: make(map[externalapi.DomainHash]model.AcceptanceData),
toDelete: make(map[externalapi.DomainHash]struct{}),
cache: lrucache.New(cacheSize),
}
}
// Stage stages the given acceptanceData for the given blockHash
func (ads *acceptanceDataStore) Stage(blockHash *externalapi.DomainHash, acceptanceData model.AcceptanceData) {
ads.staging[*blockHash] = acceptanceData
ads.staging[*blockHash] = acceptanceData.Clone()
}
func (ads *acceptanceDataStore) IsStaged() bool {
@@ -48,6 +51,7 @@ func (ads *acceptanceDataStore) Commit(dbTx model.DBTransaction) error {
if err != nil {
return err
}
ads.cache.Add(&hash, acceptanceData)
}
for hash := range ads.toDelete {
@@ -55,6 +59,7 @@ func (ads *acceptanceDataStore) Commit(dbTx model.DBTransaction) error {
if err != nil {
return err
}
ads.cache.Remove(&hash)
}
ads.Discard()
@@ -64,7 +69,11 @@ func (ads *acceptanceDataStore) Commit(dbTx model.DBTransaction) error {
// Get gets the acceptanceData associated with the given blockHash
func (ads *acceptanceDataStore) Get(dbContext model.DBReader, blockHash *externalapi.DomainHash) (model.AcceptanceData, error) {
if acceptanceData, ok := ads.staging[*blockHash]; ok {
return acceptanceData, nil
return acceptanceData.Clone(), nil
}
if acceptanceData, ok := ads.cache.Get(blockHash); ok {
return acceptanceData.(model.AcceptanceData).Clone(), nil
}
acceptanceDataBytes, err := dbContext.Get(ads.hashAsKey(blockHash))
@@ -72,7 +81,12 @@ func (ads *acceptanceDataStore) Get(dbContext model.DBReader, blockHash *externa
return nil, err
}
return ads.deserializeAcceptanceData(acceptanceDataBytes)
acceptanceData, err := ads.deserializeAcceptanceData(acceptanceDataBytes)
if err != nil {
return nil, err
}
ads.cache.Add(blockHash, acceptanceData)
return acceptanceData.Clone(), nil
}
// Delete deletes the acceptanceData associated with the given blockHash

View File

@@ -6,82 +6,134 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
)
var bucket = dbkeys.MakeBucket([]byte("block-headers"))
var countKey = dbkeys.MakeBucket().Key([]byte("block-headers-count"))
// blockHeaderStore represents a store of blocks
type blockHeaderStore struct {
staging map[externalapi.DomainHash]*externalapi.DomainBlockHeader
toDelete map[externalapi.DomainHash]struct{}
cache *lrucache.LRUCache
count uint64
}
// New instantiates a new BlockHeaderStore
func New() model.BlockHeaderStore {
return &blockHeaderStore{
func New(dbContext model.DBReader, cacheSize int) (model.BlockHeaderStore, error) {
blockHeaderStore := &blockHeaderStore{
staging: make(map[externalapi.DomainHash]*externalapi.DomainBlockHeader),
toDelete: make(map[externalapi.DomainHash]struct{}),
}
}
// Stage stages the given block header for the given blockHash
func (bms *blockHeaderStore) Stage(blockHash *externalapi.DomainHash, blockHeader *externalapi.DomainBlockHeader) {
bms.staging[*blockHash] = blockHeader
}
func (bms *blockHeaderStore) IsStaged() bool {
return len(bms.staging) != 0 || len(bms.toDelete) != 0
}
func (bms *blockHeaderStore) Discard() {
bms.staging = make(map[externalapi.DomainHash]*externalapi.DomainBlockHeader)
bms.toDelete = make(map[externalapi.DomainHash]struct{})
}
func (bms *blockHeaderStore) Commit(dbTx model.DBTransaction) error {
for hash, header := range bms.staging {
headerBytes, err := bms.serializeHeader(header)
if err != nil {
return err
}
err = dbTx.Put(bms.hashAsKey(&hash), headerBytes)
if err != nil {
return err
}
cache: lrucache.New(cacheSize),
}
for hash := range bms.toDelete {
err := dbTx.Delete(bms.hashAsKey(&hash))
if err != nil {
return err
}
}
bms.Discard()
return nil
}
// BlockHeader gets the block header associated with the given blockHash
func (bms *blockHeaderStore) BlockHeader(dbContext model.DBReader, blockHash *externalapi.DomainHash) (*externalapi.DomainBlockHeader, error) {
if header, ok := bms.staging[*blockHash]; ok {
return header, nil
}
headerBytes, err := dbContext.Get(bms.hashAsKey(blockHash))
err := blockHeaderStore.initializeCount(dbContext)
if err != nil {
return nil, err
}
return bms.deserializeHeader(headerBytes)
return blockHeaderStore, nil
}
func (bhs *blockHeaderStore) initializeCount(dbContext model.DBReader) error {
count := uint64(0)
hasCountBytes, err := dbContext.Has(countKey)
if err != nil {
return err
}
if hasCountBytes {
countBytes, err := dbContext.Get(countKey)
if err != nil {
return err
}
count, err = bhs.deserializeHeaderCount(countBytes)
if err != nil {
return err
}
}
bhs.count = count
return nil
}
// Stage stages the given block header for the given blockHash
func (bhs *blockHeaderStore) Stage(blockHash *externalapi.DomainHash, blockHeader *externalapi.DomainBlockHeader) {
bhs.staging[*blockHash] = blockHeader.Clone()
}
func (bhs *blockHeaderStore) IsStaged() bool {
return len(bhs.staging) != 0 || len(bhs.toDelete) != 0
}
func (bhs *blockHeaderStore) Discard() {
bhs.staging = make(map[externalapi.DomainHash]*externalapi.DomainBlockHeader)
bhs.toDelete = make(map[externalapi.DomainHash]struct{})
}
func (bhs *blockHeaderStore) Commit(dbTx model.DBTransaction) error {
for hash, header := range bhs.staging {
headerBytes, err := bhs.serializeHeader(header)
if err != nil {
return err
}
err = dbTx.Put(bhs.hashAsKey(&hash), headerBytes)
if err != nil {
return err
}
bhs.cache.Add(&hash, header)
}
for hash := range bhs.toDelete {
err := dbTx.Delete(bhs.hashAsKey(&hash))
if err != nil {
return err
}
bhs.cache.Remove(&hash)
}
err := bhs.commitCount(dbTx)
if err != nil {
return err
}
bhs.Discard()
return nil
}
// BlockHeader gets the block header associated with the given blockHash
func (bhs *blockHeaderStore) BlockHeader(dbContext model.DBReader, blockHash *externalapi.DomainHash) (*externalapi.DomainBlockHeader, error) {
if header, ok := bhs.staging[*blockHash]; ok {
return header.Clone(), nil
}
if header, ok := bhs.cache.Get(blockHash); ok {
return header.(*externalapi.DomainBlockHeader).Clone(), nil
}
headerBytes, err := dbContext.Get(bhs.hashAsKey(blockHash))
if err != nil {
return nil, err
}
header, err := bhs.deserializeHeader(headerBytes)
if err != nil {
return nil, err
}
bhs.cache.Add(blockHash, header)
return header.Clone(), nil
}
// HasBlock returns whether a block header with a given hash exists in the store.
func (bms *blockHeaderStore) HasBlockHeader(dbContext model.DBReader, blockHash *externalapi.DomainHash) (bool, error) {
if _, ok := bms.staging[*blockHash]; ok {
func (bhs *blockHeaderStore) HasBlockHeader(dbContext model.DBReader, blockHash *externalapi.DomainHash) (bool, error) {
if _, ok := bhs.staging[*blockHash]; ok {
return true, nil
}
exists, err := dbContext.Has(bms.hashAsKey(blockHash))
if bhs.cache.Has(blockHash) {
return true, nil
}
exists, err := dbContext.Has(bhs.hashAsKey(blockHash))
if err != nil {
return false, err
}
@@ -90,11 +142,11 @@ func (bms *blockHeaderStore) HasBlockHeader(dbContext model.DBReader, blockHash
}
// BlockHeaders gets the block headers associated with the given blockHashes
func (bms *blockHeaderStore) BlockHeaders(dbContext model.DBReader, blockHashes []*externalapi.DomainHash) ([]*externalapi.DomainBlockHeader, error) {
func (bhs *blockHeaderStore) BlockHeaders(dbContext model.DBReader, blockHashes []*externalapi.DomainHash) ([]*externalapi.DomainBlockHeader, error) {
headers := make([]*externalapi.DomainBlockHeader, len(blockHashes))
for i, hash := range blockHashes {
var err error
headers[i], err = bms.BlockHeader(dbContext, hash)
headers[i], err = bhs.BlockHeader(dbContext, hash)
if err != nil {
return nil, err
}
@@ -103,24 +155,24 @@ func (bms *blockHeaderStore) BlockHeaders(dbContext model.DBReader, blockHashes
}
// Delete deletes the block associated with the given blockHash
func (bms *blockHeaderStore) Delete(blockHash *externalapi.DomainHash) {
if _, ok := bms.staging[*blockHash]; ok {
delete(bms.staging, *blockHash)
func (bhs *blockHeaderStore) Delete(blockHash *externalapi.DomainHash) {
if _, ok := bhs.staging[*blockHash]; ok {
delete(bhs.staging, *blockHash)
return
}
bms.toDelete[*blockHash] = struct{}{}
bhs.toDelete[*blockHash] = struct{}{}
}
func (bms *blockHeaderStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey {
func (bhs *blockHeaderStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey {
return bucket.Key(hash[:])
}
func (bms *blockHeaderStore) serializeHeader(header *externalapi.DomainBlockHeader) ([]byte, error) {
func (bhs *blockHeaderStore) serializeHeader(header *externalapi.DomainBlockHeader) ([]byte, error) {
dbBlockHeader := serialization.DomainBlockHeaderToDbBlockHeader(header)
return proto.Marshal(dbBlockHeader)
}
func (bms *blockHeaderStore) deserializeHeader(headerBytes []byte) (*externalapi.DomainBlockHeader, error) {
func (bhs *blockHeaderStore) deserializeHeader(headerBytes []byte) (*externalapi.DomainBlockHeader, error) {
dbBlockHeader := &serialization.DbBlockHeader{}
err := proto.Unmarshal(headerBytes, dbBlockHeader)
if err != nil {
@@ -128,3 +180,35 @@ func (bms *blockHeaderStore) deserializeHeader(headerBytes []byte) (*externalapi
}
return serialization.DbBlockHeaderToDomainBlockHeader(dbBlockHeader)
}
func (bhs *blockHeaderStore) Count() uint64 {
return bhs.count + uint64(len(bhs.staging)) - uint64(len(bhs.toDelete))
}
func (bhs *blockHeaderStore) deserializeHeaderCount(countBytes []byte) (uint64, error) {
dbBlockHeaderCount := &serialization.DbBlockHeaderCount{}
err := proto.Unmarshal(countBytes, dbBlockHeaderCount)
if err != nil {
return 0, err
}
return dbBlockHeaderCount.Count, nil
}
func (bhs *blockHeaderStore) commitCount(dbTx model.DBTransaction) error {
count := bhs.Count()
countBytes, err := bhs.serializeHeaderCount(count)
if err != nil {
return err
}
err = dbTx.Put(countKey, countBytes)
if err != nil {
return err
}
bhs.count = count
return nil
}
func (bhs *blockHeaderStore) serializeHeaderCount(count uint64) ([]byte, error) {
dbBlockHeaderCount := &serialization.DbBlockHeaderCount{Count: count}
return proto.Marshal(dbBlockHeaderCount)
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
)
var bucket = dbkeys.MakeBucket([]byte("block-relations"))
@@ -13,17 +14,19 @@ var bucket = dbkeys.MakeBucket([]byte("block-relations"))
// blockRelationStore represents a store of BlockRelations
type blockRelationStore struct {
staging map[externalapi.DomainHash]*model.BlockRelations
cache *lrucache.LRUCache
}
// New instantiates a new BlockRelationStore
func New() model.BlockRelationStore {
func New(cacheSize int) model.BlockRelationStore {
return &blockRelationStore{
staging: make(map[externalapi.DomainHash]*model.BlockRelations),
cache: lrucache.New(cacheSize),
}
}
func (brs *blockRelationStore) StageBlockRelation(blockHash *externalapi.DomainHash, blockRelations *model.BlockRelations) {
brs.staging[*blockHash] = blockRelations
brs.staging[*blockHash] = blockRelations.Clone()
}
func (brs *blockRelationStore) IsStaged() bool {
@@ -44,6 +47,7 @@ func (brs *blockRelationStore) Commit(dbTx model.DBTransaction) error {
if err != nil {
return err
}
brs.cache.Add(&hash, blockRelations)
}
brs.Discard()
@@ -52,7 +56,11 @@ func (brs *blockRelationStore) Commit(dbTx model.DBTransaction) error {
func (brs *blockRelationStore) BlockRelation(dbContext model.DBReader, blockHash *externalapi.DomainHash) (*model.BlockRelations, error) {
if blockRelations, ok := brs.staging[*blockHash]; ok {
return blockRelations, nil
return blockRelations.Clone(), nil
}
if blockRelations, ok := brs.cache.Get(blockHash); ok {
return blockRelations.(*model.BlockRelations).Clone(), nil
}
blockRelationsBytes, err := dbContext.Get(brs.hashAsKey(blockHash))
@@ -60,7 +68,12 @@ func (brs *blockRelationStore) BlockRelation(dbContext model.DBReader, blockHash
return nil, err
}
return brs.deserializeBlockRelations(blockRelationsBytes)
blockRelations, err := brs.deserializeBlockRelations(blockRelationsBytes)
if err != nil {
return nil, err
}
brs.cache.Add(blockHash, blockRelations)
return blockRelations.Clone(), nil
}
func (brs *blockRelationStore) Has(dbContext model.DBReader, blockHash *externalapi.DomainHash) (bool, error) {
@@ -68,6 +81,10 @@ func (brs *blockRelationStore) Has(dbContext model.DBReader, blockHash *external
return true, nil
}
if brs.cache.Has(blockHash) {
return true, nil
}
return dbContext.Has(brs.hashAsKey(blockHash))
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
)
var bucket = dbkeys.MakeBucket([]byte("block-statuses"))
@@ -13,18 +14,20 @@ var bucket = dbkeys.MakeBucket([]byte("block-statuses"))
// blockStatusStore represents a store of BlockStatuses
type blockStatusStore struct {
staging map[externalapi.DomainHash]externalapi.BlockStatus
cache *lrucache.LRUCache
}
// New instantiates a new BlockStatusStore
func New() model.BlockStatusStore {
func New(cacheSize int) model.BlockStatusStore {
return &blockStatusStore{
staging: make(map[externalapi.DomainHash]externalapi.BlockStatus),
cache: lrucache.New(cacheSize),
}
}
// Stage stages the given blockStatus for the given blockHash
func (bss *blockStatusStore) Stage(blockHash *externalapi.DomainHash, blockStatus externalapi.BlockStatus) {
bss.staging[*blockHash] = blockStatus
bss.staging[*blockHash] = blockStatus.Clone()
}
func (bss *blockStatusStore) IsStaged() bool {
@@ -45,6 +48,7 @@ func (bss *blockStatusStore) Commit(dbTx model.DBTransaction) error {
if err != nil {
return err
}
bss.cache.Add(&hash, status)
}
bss.Discard()
@@ -57,12 +61,21 @@ func (bss *blockStatusStore) Get(dbContext model.DBReader, blockHash *externalap
return status, nil
}
if status, ok := bss.cache.Get(blockHash); ok {
return status.(externalapi.BlockStatus), nil
}
statusBytes, err := dbContext.Get(bss.hashAsKey(blockHash))
if err != nil {
return 0, err
}
return bss.deserializeHeader(statusBytes)
status, err := bss.deserializeBlockStatus(statusBytes)
if err != nil {
return 0, err
}
bss.cache.Add(blockHash, status)
return status, nil
}
// Exists returns true if the blockStatus for the given blockHash exists
@@ -71,6 +84,10 @@ func (bss *blockStatusStore) Exists(dbContext model.DBReader, blockHash *externa
return true, nil
}
if bss.cache.Has(blockHash) {
return true, nil
}
exists, err := dbContext.Has(bss.hashAsKey(blockHash))
if err != nil {
return false, err
@@ -84,7 +101,7 @@ func (bss *blockStatusStore) serializeBlockStatus(status externalapi.BlockStatus
return proto.Marshal(dbBlockStatus)
}
func (bss *blockStatusStore) deserializeHeader(statusBytes []byte) (externalapi.BlockStatus, error) {
func (bss *blockStatusStore) deserializeBlockStatus(statusBytes []byte) (externalapi.BlockStatus, error) {
dbBlockStatus := &serialization.DbBlockStatus{}
err := proto.Unmarshal(statusBytes, dbBlockStatus)
if err != nil {

View File

@@ -6,82 +6,134 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
)
var bucket = dbkeys.MakeBucket([]byte("blocks"))
var countKey = dbkeys.MakeBucket().Key([]byte("blocks-count"))
// blockStore represents a store of blocks
type blockStore struct {
staging map[externalapi.DomainHash]*externalapi.DomainBlock
toDelete map[externalapi.DomainHash]struct{}
cache *lrucache.LRUCache
count uint64
}
// New instantiates a new BlockStore
func New() model.BlockStore {
return &blockStore{
func New(dbContext model.DBReader, cacheSize int) (model.BlockStore, error) {
blockStore := &blockStore{
staging: make(map[externalapi.DomainHash]*externalapi.DomainBlock),
toDelete: make(map[externalapi.DomainHash]struct{}),
}
}
// Stage stages the given block for the given blockHash
func (bms *blockStore) Stage(blockHash *externalapi.DomainHash, block *externalapi.DomainBlock) {
bms.staging[*blockHash] = block
}
func (bms *blockStore) IsStaged() bool {
return len(bms.staging) != 0 || len(bms.toDelete) != 0
}
func (bms *blockStore) Discard() {
bms.staging = make(map[externalapi.DomainHash]*externalapi.DomainBlock)
bms.toDelete = make(map[externalapi.DomainHash]struct{})
}
func (bms *blockStore) Commit(dbTx model.DBTransaction) error {
for hash, block := range bms.staging {
blockBytes, err := bms.serializeBlock(block)
if err != nil {
return err
}
err = dbTx.Put(bms.hashAsKey(&hash), blockBytes)
if err != nil {
return err
}
cache: lrucache.New(cacheSize),
}
for hash := range bms.toDelete {
err := dbTx.Delete(bms.hashAsKey(&hash))
if err != nil {
return err
}
}
bms.Discard()
return nil
}
// Block gets the block associated with the given blockHash
func (bms *blockStore) Block(dbContext model.DBReader, blockHash *externalapi.DomainHash) (*externalapi.DomainBlock, error) {
if block, ok := bms.staging[*blockHash]; ok {
return block, nil
}
blockBytes, err := dbContext.Get(bms.hashAsKey(blockHash))
err := blockStore.initializeCount(dbContext)
if err != nil {
return nil, err
}
return bms.deserializeBlock(blockBytes)
return blockStore, nil
}
func (bs *blockStore) initializeCount(dbContext model.DBReader) error {
count := uint64(0)
hasCountBytes, err := dbContext.Has(countKey)
if err != nil {
return err
}
if hasCountBytes {
countBytes, err := dbContext.Get(countKey)
if err != nil {
return err
}
count, err = bs.deserializeBlockCount(countBytes)
if err != nil {
return err
}
}
bs.count = count
return nil
}
// Stage stages the given block for the given blockHash
func (bs *blockStore) Stage(blockHash *externalapi.DomainHash, block *externalapi.DomainBlock) {
bs.staging[*blockHash] = block.Clone()
}
func (bs *blockStore) IsStaged() bool {
return len(bs.staging) != 0 || len(bs.toDelete) != 0
}
func (bs *blockStore) Discard() {
bs.staging = make(map[externalapi.DomainHash]*externalapi.DomainBlock)
bs.toDelete = make(map[externalapi.DomainHash]struct{})
}
func (bs *blockStore) Commit(dbTx model.DBTransaction) error {
for hash, block := range bs.staging {
blockBytes, err := bs.serializeBlock(block)
if err != nil {
return err
}
err = dbTx.Put(bs.hashAsKey(&hash), blockBytes)
if err != nil {
return err
}
bs.cache.Add(&hash, block)
}
for hash := range bs.toDelete {
err := dbTx.Delete(bs.hashAsKey(&hash))
if err != nil {
return err
}
bs.cache.Remove(&hash)
}
err := bs.commitCount(dbTx)
if err != nil {
return err
}
bs.Discard()
return nil
}
// Block gets the block associated with the given blockHash
func (bs *blockStore) Block(dbContext model.DBReader, blockHash *externalapi.DomainHash) (*externalapi.DomainBlock, error) {
if block, ok := bs.staging[*blockHash]; ok {
return block.Clone(), nil
}
if block, ok := bs.cache.Get(blockHash); ok {
return block.(*externalapi.DomainBlock).Clone(), nil
}
blockBytes, err := dbContext.Get(bs.hashAsKey(blockHash))
if err != nil {
return nil, err
}
block, err := bs.deserializeBlock(blockBytes)
if err != nil {
return nil, err
}
bs.cache.Add(blockHash, block)
return block.Clone(), nil
}
// HasBlock returns whether a block with a given hash exists in the store.
func (bms *blockStore) HasBlock(dbContext model.DBReader, blockHash *externalapi.DomainHash) (bool, error) {
if _, ok := bms.staging[*blockHash]; ok {
func (bs *blockStore) HasBlock(dbContext model.DBReader, blockHash *externalapi.DomainHash) (bool, error) {
if _, ok := bs.staging[*blockHash]; ok {
return true, nil
}
exists, err := dbContext.Has(bms.hashAsKey(blockHash))
if bs.cache.Has(blockHash) {
return true, nil
}
exists, err := dbContext.Has(bs.hashAsKey(blockHash))
if err != nil {
return false, err
}
@@ -90,11 +142,11 @@ func (bms *blockStore) HasBlock(dbContext model.DBReader, blockHash *externalapi
}
// Blocks gets the blocks associated with the given blockHashes
func (bms *blockStore) Blocks(dbContext model.DBReader, blockHashes []*externalapi.DomainHash) ([]*externalapi.DomainBlock, error) {
func (bs *blockStore) Blocks(dbContext model.DBReader, blockHashes []*externalapi.DomainHash) ([]*externalapi.DomainBlock, error) {
blocks := make([]*externalapi.DomainBlock, len(blockHashes))
for i, hash := range blockHashes {
var err error
blocks[i], err = bms.Block(dbContext, hash)
blocks[i], err = bs.Block(dbContext, hash)
if err != nil {
return nil, err
}
@@ -103,20 +155,20 @@ func (bms *blockStore) Blocks(dbContext model.DBReader, blockHashes []*externala
}
// Delete deletes the block associated with the given blockHash
func (bms *blockStore) Delete(blockHash *externalapi.DomainHash) {
if _, ok := bms.staging[*blockHash]; ok {
delete(bms.staging, *blockHash)
func (bs *blockStore) Delete(blockHash *externalapi.DomainHash) {
if _, ok := bs.staging[*blockHash]; ok {
delete(bs.staging, *blockHash)
return
}
bms.toDelete[*blockHash] = struct{}{}
bs.toDelete[*blockHash] = struct{}{}
}
func (bms *blockStore) serializeBlock(block *externalapi.DomainBlock) ([]byte, error) {
func (bs *blockStore) serializeBlock(block *externalapi.DomainBlock) ([]byte, error) {
dbBlock := serialization.DomainBlockToDbBlock(block)
return proto.Marshal(dbBlock)
}
func (bms *blockStore) deserializeBlock(blockBytes []byte) (*externalapi.DomainBlock, error) {
func (bs *blockStore) deserializeBlock(blockBytes []byte) (*externalapi.DomainBlock, error) {
dbBlock := &serialization.DbBlock{}
err := proto.Unmarshal(blockBytes, dbBlock)
if err != nil {
@@ -125,6 +177,38 @@ func (bms *blockStore) deserializeBlock(blockBytes []byte) (*externalapi.DomainB
return serialization.DbBlockToDomainBlock(dbBlock)
}
func (bms *blockStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey {
func (bs *blockStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey {
return bucket.Key(hash[:])
}
func (bs *blockStore) Count() uint64 {
return bs.count + uint64(len(bs.staging)) - uint64(len(bs.toDelete))
}
func (bs *blockStore) deserializeBlockCount(countBytes []byte) (uint64, error) {
dbBlockCount := &serialization.DbBlockCount{}
err := proto.Unmarshal(countBytes, dbBlockCount)
if err != nil {
return 0, err
}
return dbBlockCount.Count, nil
}
func (bs *blockStore) commitCount(dbTx model.DBTransaction) error {
count := bs.Count()
countBytes, err := bs.serializeBlockCount(count)
if err != nil {
return err
}
err = dbTx.Put(countKey, countBytes)
if err != nil {
return err
}
bs.count = count
return nil
}
func (bs *blockStore) serializeBlockCount(count uint64) ([]byte, error) {
dbBlockCount := &serialization.DbBlockCount{Count: count}
return proto.Marshal(dbBlockCount)
}

View File

@@ -7,10 +7,13 @@ import (
// consensusStateStore represents a store for the current consensus state
type consensusStateStore struct {
stagedTips []*externalapi.DomainHash
stagedVirtualDiffParents []*externalapi.DomainHash
stagedVirtualUTXODiff *model.UTXODiff
stagedVirtualUTXOSet model.UTXOCollection
tipsStaging []*externalapi.DomainHash
virtualDiffParentsStaging []*externalapi.DomainHash
virtualUTXODiffStaging *model.UTXODiff
virtualUTXOSetStaging model.UTXOCollection
tipsCache []*externalapi.DomainHash
virtualDiffParentsCache []*externalapi.DomainHash
}
// New instantiates a new ConsensusStateStore
@@ -18,40 +21,40 @@ func New() model.ConsensusStateStore {
return &consensusStateStore{}
}
func (c *consensusStateStore) Discard() {
c.stagedTips = nil
c.stagedVirtualUTXODiff = nil
c.stagedVirtualDiffParents = nil
c.stagedVirtualUTXOSet = nil
func (css *consensusStateStore) Discard() {
css.tipsStaging = nil
css.virtualUTXODiffStaging = nil
css.virtualDiffParentsStaging = nil
css.virtualUTXOSetStaging = nil
}
func (c *consensusStateStore) Commit(dbTx model.DBTransaction) error {
err := c.commitTips(dbTx)
func (css *consensusStateStore) Commit(dbTx model.DBTransaction) error {
err := css.commitTips(dbTx)
if err != nil {
return err
}
err = c.commitVirtualDiffParents(dbTx)
err = css.commitVirtualDiffParents(dbTx)
if err != nil {
return err
}
err = c.commitVirtualUTXODiff(dbTx)
err = css.commitVirtualUTXODiff(dbTx)
if err != nil {
return err
}
err = c.commitVirtualUTXOSet(dbTx)
err = css.commitVirtualUTXOSet(dbTx)
if err != nil {
return err
}
c.Discard()
css.Discard()
return nil
}
func (c *consensusStateStore) IsStaged() bool {
return c.stagedTips != nil ||
c.stagedVirtualDiffParents != nil ||
c.stagedVirtualUTXODiff != nil
func (css *consensusStateStore) IsStaged() bool {
return css.tipsStaging != nil ||
css.virtualDiffParentsStaging != nil ||
css.virtualUTXODiffStaging != nil
}

View File

@@ -1,17 +1,22 @@
package consensusstatestore
import (
"github.com/golang/protobuf/proto"
"github.com/kaspanet/kaspad/domain/consensus/database/serialization"
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
)
var tipsKey = dbkeys.MakeBucket().Key([]byte("tips"))
func (c *consensusStateStore) Tips(dbContext model.DBReader) ([]*externalapi.DomainHash, error) {
if c.stagedTips != nil {
return c.stagedTips, nil
func (css *consensusStateStore) Tips(dbContext model.DBReader) ([]*externalapi.DomainHash, error) {
if css.tipsStaging != nil {
return externalapi.CloneHashes(css.tipsStaging), nil
}
if css.tipsCache != nil {
return externalapi.CloneHashes(css.tipsCache), nil
}
tipsBytes, err := dbContext.Get(tipsKey)
@@ -19,20 +24,51 @@ func (c *consensusStateStore) Tips(dbContext model.DBReader) ([]*externalapi.Dom
return nil, err
}
return hashes.DeserializeHashSlice(tipsBytes)
tips, err := css.deserializeTips(tipsBytes)
if err != nil {
return nil, err
}
css.tipsCache = tips
return externalapi.CloneHashes(tips), nil
}
func (c *consensusStateStore) StageTips(tipHashes []*externalapi.DomainHash) {
c.stagedTips = tipHashes
func (css *consensusStateStore) StageTips(tipHashes []*externalapi.DomainHash) {
css.tipsStaging = externalapi.CloneHashes(tipHashes)
}
func (c *consensusStateStore) commitTips(dbTx model.DBTransaction) error {
tipsBytes := hashes.SerializeHashSlice(c.stagedTips)
func (css *consensusStateStore) commitTips(dbTx model.DBTransaction) error {
if css.tipsStaging == nil {
return nil
}
err := dbTx.Put(tipsKey, tipsBytes)
tipsBytes, err := css.serializeTips(css.tipsStaging)
if err != nil {
return err
}
err = dbTx.Put(tipsKey, tipsBytes)
if err != nil {
return err
}
css.tipsCache = css.tipsStaging
// Note: we don't discard the staging here since that's
// being done at the end of Commit()
return nil
}
func (css *consensusStateStore) serializeTips(tips []*externalapi.DomainHash) ([]byte, error) {
dbTips := serialization.TipsToDBTips(tips)
return proto.Marshal(dbTips)
}
func (css *consensusStateStore) deserializeTips(tipsBytes []byte) ([]*externalapi.DomainHash,
error) {
dbTips := &serialization.DbTips{}
err := proto.Unmarshal(tipsBytes, dbTips)
if err != nil {
return nil, err
}
return serialization.DBTipsToTips(dbTips)
}

View File

@@ -4,6 +4,7 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
"github.com/pkg/errors"
)
@@ -18,25 +19,21 @@ func utxoKey(outpoint *externalapi.DomainOutpoint) (model.DBKey, error) {
return utxoSetBucket.Key(serializedOutpoint), nil
}
func (c *consensusStateStore) StageVirtualUTXODiff(virtualUTXODiff *model.UTXODiff) error {
if c.stagedVirtualUTXOSet != nil {
func (css *consensusStateStore) StageVirtualUTXODiff(virtualUTXODiff *model.UTXODiff) error {
if css.virtualUTXOSetStaging != nil {
return errors.New("cannot stage virtual UTXO diff while virtual UTXO set is staged")
}
c.stagedVirtualUTXODiff = virtualUTXODiff
css.virtualUTXODiffStaging = virtualUTXODiff.Clone()
return nil
}
func (c *consensusStateStore) commitVirtualUTXODiff(dbTx model.DBTransaction) error {
if c.stagedVirtualUTXOSet != nil {
return errors.New("cannot commit virtual UTXO diff while virtual UTXO set is staged")
}
if c.stagedVirtualUTXODiff == nil {
func (css *consensusStateStore) commitVirtualUTXODiff(dbTx model.DBTransaction) error {
if css.virtualUTXODiffStaging == nil {
return nil
}
for toRemoveOutpoint := range c.stagedVirtualUTXODiff.ToRemove {
for toRemoveOutpoint := range css.virtualUTXODiffStaging.ToRemove {
dbKey, err := utxoKey(&toRemoveOutpoint)
if err != nil {
return err
@@ -47,7 +44,7 @@ func (c *consensusStateStore) commitVirtualUTXODiff(dbTx model.DBTransaction) er
}
}
for toAddOutpoint, toAddEntry := range c.stagedVirtualUTXODiff.ToAdd {
for toAddOutpoint, toAddEntry := range css.virtualUTXODiffStaging.ToAdd {
dbKey, err := utxoKey(&toAddOutpoint)
if err != nil {
return err
@@ -62,15 +59,17 @@ func (c *consensusStateStore) commitVirtualUTXODiff(dbTx model.DBTransaction) er
}
}
// Note: we don't discard the staging here since that's
// being done at the end of Commit()
return nil
}
func (c *consensusStateStore) commitVirtualUTXOSet(dbTx model.DBTransaction) error {
if c.stagedVirtualUTXODiff != nil {
return errors.New("cannot commit virtual UTXO set while virtual UTXO diff is staged")
func (css *consensusStateStore) commitVirtualUTXOSet(dbTx model.DBTransaction) error {
if css.virtualUTXOSetStaging == nil {
return nil
}
for outpoint, utxoEntry := range c.stagedVirtualUTXOSet {
for outpoint, utxoEntry := range css.virtualUTXOSetStaging {
dbKey, err := utxoKey(&outpoint)
if err != nil {
return err
@@ -85,28 +84,30 @@ func (c *consensusStateStore) commitVirtualUTXOSet(dbTx model.DBTransaction) err
}
}
// Note: we don't discard the staging here since that's
// being done at the end of Commit()
return nil
}
func (c *consensusStateStore) UTXOByOutpoint(dbContext model.DBReader, outpoint *externalapi.DomainOutpoint) (
func (css *consensusStateStore) UTXOByOutpoint(dbContext model.DBReader, outpoint *externalapi.DomainOutpoint) (
*externalapi.UTXOEntry, error) {
if c.stagedVirtualUTXOSet != nil {
return c.utxoByOutpointFromStagedVirtualUTXOSet(outpoint)
if css.virtualUTXOSetStaging != nil {
return css.utxoByOutpointFromStagedVirtualUTXOSet(outpoint)
}
return c.utxoByOutpointFromStagedVirtualUTXODiff(dbContext, outpoint)
return css.utxoByOutpointFromStagedVirtualUTXODiff(dbContext, outpoint)
}
func (c *consensusStateStore) utxoByOutpointFromStagedVirtualUTXODiff(dbContext model.DBReader,
func (css *consensusStateStore) utxoByOutpointFromStagedVirtualUTXODiff(dbContext model.DBReader,
outpoint *externalapi.DomainOutpoint) (
*externalapi.UTXOEntry, error) {
if c.stagedVirtualUTXODiff != nil {
if _, ok := c.stagedVirtualUTXODiff.ToRemove[*outpoint]; ok {
if css.virtualUTXODiffStaging != nil {
if _, ok := css.virtualUTXODiffStaging.ToRemove[*outpoint]; ok {
return nil, errors.Errorf("outpoint was not found")
}
if utxoEntry, ok := c.stagedVirtualUTXODiff.ToAdd[*outpoint]; ok {
if utxoEntry, ok := css.virtualUTXODiffStaging.ToAdd[*outpoint]; ok {
return utxoEntry, nil
}
}
@@ -124,30 +125,33 @@ func (c *consensusStateStore) utxoByOutpointFromStagedVirtualUTXODiff(dbContext
return deserializeUTXOEntry(serializedUTXOEntry)
}
func (c *consensusStateStore) utxoByOutpointFromStagedVirtualUTXOSet(outpoint *externalapi.DomainOutpoint) (
func (css *consensusStateStore) utxoByOutpointFromStagedVirtualUTXOSet(outpoint *externalapi.DomainOutpoint) (
*externalapi.UTXOEntry, error) {
if utxoEntry, ok := c.stagedVirtualUTXOSet[*outpoint]; ok {
if utxoEntry, ok := css.virtualUTXOSetStaging[*outpoint]; ok {
return utxoEntry, nil
}
return nil, errors.Errorf("outpoint was not found")
}
func (c *consensusStateStore) HasUTXOByOutpoint(dbContext model.DBReader, outpoint *externalapi.DomainOutpoint) (bool, error) {
if c.stagedVirtualUTXOSet != nil {
return c.hasUTXOByOutpointFromStagedVirtualUTXOSet(outpoint), nil
func (css *consensusStateStore) HasUTXOByOutpoint(dbContext model.DBReader, outpoint *externalapi.DomainOutpoint) (bool, error) {
if css.virtualUTXOSetStaging != nil {
return css.hasUTXOByOutpointFromStagedVirtualUTXOSet(outpoint), nil
}
return c.hasUTXOByOutpointFromStagedVirtualUTXODiff(dbContext, outpoint)
return css.hasUTXOByOutpointFromStagedVirtualUTXODiff(dbContext, outpoint)
}
func (c *consensusStateStore) hasUTXOByOutpointFromStagedVirtualUTXODiff(dbContext model.DBReader,
func (css *consensusStateStore) hasUTXOByOutpointFromStagedVirtualUTXODiff(dbContext model.DBReader,
outpoint *externalapi.DomainOutpoint) (bool, error) {
if _, ok := c.stagedVirtualUTXODiff.ToRemove[*outpoint]; ok {
return false, nil
}
if _, ok := c.stagedVirtualUTXODiff.ToAdd[*outpoint]; ok {
return true, nil
if css.virtualUTXODiffStaging != nil {
if _, ok := css.virtualUTXODiffStaging.ToRemove[*outpoint]; ok {
return false, nil
}
if _, ok := css.virtualUTXODiffStaging.ToAdd[*outpoint]; ok {
return true, nil
}
}
key, err := utxoKey(outpoint)
@@ -158,25 +162,30 @@ func (c *consensusStateStore) hasUTXOByOutpointFromStagedVirtualUTXODiff(dbConte
return dbContext.Has(key)
}
func (c *consensusStateStore) hasUTXOByOutpointFromStagedVirtualUTXOSet(outpoint *externalapi.DomainOutpoint) bool {
_, ok := c.stagedVirtualUTXOSet[*outpoint]
func (css *consensusStateStore) hasUTXOByOutpointFromStagedVirtualUTXOSet(outpoint *externalapi.DomainOutpoint) bool {
_, ok := css.virtualUTXOSetStaging[*outpoint]
return ok
}
func (c *consensusStateStore) VirtualUTXOSetIterator(dbContext model.DBReader) (model.ReadOnlyUTXOSetIterator, error) {
func (css *consensusStateStore) VirtualUTXOSetIterator(dbContext model.DBReader) (model.ReadOnlyUTXOSetIterator, error) {
cursor, err := dbContext.Cursor(utxoSetBucket)
if err != nil {
return nil, err
}
return newUTXOSetIterator(cursor), nil
mainIterator := newCursorUTXOSetIterator(cursor)
if css.virtualUTXODiffStaging != nil {
return utxo.IteratorWithDiff(mainIterator, css.virtualUTXODiffStaging)
}
return mainIterator, nil
}
type utxoSetIterator struct {
cursor model.DBCursor
}
func newUTXOSetIterator(cursor model.DBCursor) model.ReadOnlyUTXOSetIterator {
func newCursorUTXOSetIterator(cursor model.DBCursor) model.ReadOnlyUTXOSetIterator {
return &utxoSetIterator{cursor: cursor}
}
@@ -208,22 +217,22 @@ func (u utxoSetIterator) Get() (outpoint *externalapi.DomainOutpoint, utxoEntry
return outpoint, utxoEntry, nil
}
func (c *consensusStateStore) StageVirtualUTXOSet(virtualUTXOSetIterator model.ReadOnlyUTXOSetIterator) error {
if c.stagedVirtualUTXODiff != nil {
func (css *consensusStateStore) StageVirtualUTXOSet(virtualUTXOSetIterator model.ReadOnlyUTXOSetIterator) error {
if css.virtualUTXODiffStaging != nil {
return errors.New("cannot stage virtual UTXO set while virtual UTXO diff is staged")
}
c.stagedVirtualUTXOSet = make(model.UTXOCollection)
css.virtualUTXOSetStaging = make(model.UTXOCollection)
for virtualUTXOSetIterator.Next() {
outpoint, entry, err := virtualUTXOSetIterator.Get()
if err != nil {
return err
}
if _, exists := c.stagedVirtualUTXOSet[*outpoint]; exists {
if _, exists := css.virtualUTXOSetStaging[*outpoint]; exists {
return errors.Errorf("outpoint %s is found more than once in the given iterator", outpoint)
}
c.stagedVirtualUTXOSet[*outpoint] = entry
css.virtualUTXOSetStaging[*outpoint] = entry
}
return nil

View File

@@ -1,17 +1,22 @@
package consensusstatestore
import (
"github.com/golang/protobuf/proto"
"github.com/kaspanet/kaspad/domain/consensus/database/serialization"
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
)
var virtualDiffParentsKey = dbkeys.MakeBucket().Key([]byte("virtual-diff-parents"))
func (c *consensusStateStore) VirtualDiffParents(dbContext model.DBReader) ([]*externalapi.DomainHash, error) {
if c.stagedVirtualDiffParents != nil {
return c.stagedVirtualDiffParents, nil
func (css *consensusStateStore) VirtualDiffParents(dbContext model.DBReader) ([]*externalapi.DomainHash, error) {
if css.virtualDiffParentsStaging != nil {
return externalapi.CloneHashes(css.virtualDiffParentsStaging), nil
}
if css.virtualDiffParentsCache != nil {
return externalapi.CloneHashes(css.virtualDiffParentsCache), nil
}
virtualDiffParentsBytes, err := dbContext.Get(virtualDiffParentsKey)
@@ -19,20 +24,51 @@ func (c *consensusStateStore) VirtualDiffParents(dbContext model.DBReader) ([]*e
return nil, err
}
return hashes.DeserializeHashSlice(virtualDiffParentsBytes)
virtualDiffParents, err := css.deserializeVirtualDiffParents(virtualDiffParentsBytes)
if err != nil {
return nil, err
}
css.virtualDiffParentsCache = virtualDiffParents
return externalapi.CloneHashes(virtualDiffParents), nil
}
func (c *consensusStateStore) StageVirtualDiffParents(tipHashes []*externalapi.DomainHash) {
c.stagedVirtualDiffParents = tipHashes
func (css *consensusStateStore) StageVirtualDiffParents(tipHashes []*externalapi.DomainHash) {
css.virtualDiffParentsStaging = externalapi.CloneHashes(tipHashes)
}
func (c *consensusStateStore) commitVirtualDiffParents(dbTx model.DBTransaction) error {
virtualDiffParentsBytes := hashes.SerializeHashSlice(c.stagedVirtualDiffParents)
func (css *consensusStateStore) commitVirtualDiffParents(dbTx model.DBTransaction) error {
if css.virtualDiffParentsStaging == nil {
return nil
}
err := dbTx.Put(virtualDiffParentsKey, virtualDiffParentsBytes)
virtualDiffParentsBytes, err := css.serializeVirtualDiffParents(css.virtualDiffParentsStaging)
if err != nil {
return err
}
err = dbTx.Put(virtualDiffParentsKey, virtualDiffParentsBytes)
if err != nil {
return err
}
css.virtualDiffParentsCache = css.virtualDiffParentsStaging
// Note: we don't discard the staging here since that's
// being done at the end of Commit()
return nil
}
func (css *consensusStateStore) serializeVirtualDiffParents(virtualDiffParentsBytes []*externalapi.DomainHash) ([]byte, error) {
virtualDiffParents := serialization.VirtualDiffParentsToDBHeaderVirtualDiffParents(virtualDiffParentsBytes)
return proto.Marshal(virtualDiffParents)
}
func (css *consensusStateStore) deserializeVirtualDiffParents(virtualDiffParentsBytes []byte) ([]*externalapi.DomainHash,
error) {
dbVirtualDiffParents := &serialization.DbVirtualDiffParents{}
err := proto.Unmarshal(virtualDiffParentsBytes, dbVirtualDiffParents)
if err != nil {
return nil, err
}
return serialization.DBVirtualDiffParentsToVirtualDiffParents(dbVirtualDiffParents)
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
)
var bucket = dbkeys.MakeBucket([]byte("block-ghostdag-data"))
@@ -13,18 +14,20 @@ var bucket = dbkeys.MakeBucket([]byte("block-ghostdag-data"))
// ghostdagDataStore represents a store of BlockGHOSTDAGData
type ghostdagDataStore struct {
staging map[externalapi.DomainHash]*model.BlockGHOSTDAGData
cache *lrucache.LRUCache
}
// New instantiates a new GHOSTDAGDataStore
func New() model.GHOSTDAGDataStore {
func New(cacheSize int) model.GHOSTDAGDataStore {
return &ghostdagDataStore{
staging: make(map[externalapi.DomainHash]*model.BlockGHOSTDAGData),
cache: lrucache.New(cacheSize),
}
}
// Stage stages the given blockGHOSTDAGData for the given blockHash
func (gds *ghostdagDataStore) Stage(blockHash *externalapi.DomainHash, blockGHOSTDAGData *model.BlockGHOSTDAGData) {
gds.staging[*blockHash] = blockGHOSTDAGData
gds.staging[*blockHash] = blockGHOSTDAGData.Clone()
}
func (gds *ghostdagDataStore) IsStaged() bool {
@@ -41,11 +44,11 @@ func (gds *ghostdagDataStore) Commit(dbTx model.DBTransaction) error {
if err != nil {
return err
}
err = dbTx.Put(gds.hashAsKey(&hash), blockGhostdagDataBytes)
if err != nil {
return err
}
gds.cache.Add(&hash, blockGHOSTDAGData)
}
gds.Discard()
@@ -55,7 +58,11 @@ func (gds *ghostdagDataStore) Commit(dbTx model.DBTransaction) error {
// Get gets the blockGHOSTDAGData associated with the given blockHash
func (gds *ghostdagDataStore) Get(dbContext model.DBReader, blockHash *externalapi.DomainHash) (*model.BlockGHOSTDAGData, error) {
if blockGHOSTDAGData, ok := gds.staging[*blockHash]; ok {
return blockGHOSTDAGData, nil
return blockGHOSTDAGData.Clone(), nil
}
if blockGHOSTDAGData, ok := gds.cache.Get(blockHash); ok {
return blockGHOSTDAGData.(*model.BlockGHOSTDAGData).Clone(), nil
}
blockGHOSTDAGDataBytes, err := dbContext.Get(gds.hashAsKey(blockHash))
@@ -63,7 +70,12 @@ func (gds *ghostdagDataStore) Get(dbContext model.DBReader, blockHash *externala
return nil, err
}
return gds.deserializeBlockGHOSTDAGData(blockGHOSTDAGDataBytes)
blockGHOSTDAGData, err := gds.deserializeBlockGHOSTDAGData(blockGHOSTDAGDataBytes)
if err != nil {
return nil, err
}
gds.cache.Add(blockHash, blockGHOSTDAGData)
return blockGHOSTDAGData.Clone(), nil
}
func (gds *ghostdagDataStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey {

View File

@@ -12,50 +12,64 @@ var headerTipsKey = dbkeys.MakeBucket().Key([]byte("header-tips"))
type headerTipsStore struct {
staging []*externalapi.DomainHash
cache []*externalapi.DomainHash
}
func (h *headerTipsStore) HasTips(dbContext model.DBReader) (bool, error) {
if h.staging != nil {
return len(h.staging) > 0, nil
// New instantiates a new HeaderTipsStore
func New() model.HeaderTipsStore {
return &headerTipsStore{}
}
func (hts *headerTipsStore) HasTips(dbContext model.DBReader) (bool, error) {
if len(hts.staging) > 0 {
return true, nil
}
if len(hts.cache) > 0 {
return true, nil
}
return dbContext.Has(headerTipsKey)
}
func (h *headerTipsStore) Discard() {
h.staging = nil
func (hts *headerTipsStore) Discard() {
hts.staging = nil
}
func (h *headerTipsStore) Commit(dbTx model.DBTransaction) error {
if h.staging == nil {
func (hts *headerTipsStore) Commit(dbTx model.DBTransaction) error {
if hts.staging == nil {
return nil
}
tipsBytes, err := h.serializeTips(h.staging)
tipsBytes, err := hts.serializeTips(hts.staging)
if err != nil {
return err
}
err = dbTx.Put(headerTipsKey, tipsBytes)
if err != nil {
return err
}
hts.cache = hts.staging
h.Discard()
hts.Discard()
return nil
}
func (h *headerTipsStore) Stage(tips []*externalapi.DomainHash) {
h.staging = tips
func (hts *headerTipsStore) Stage(tips []*externalapi.DomainHash) {
hts.staging = externalapi.CloneHashes(tips)
}
func (h *headerTipsStore) IsStaged() bool {
return h.staging != nil
func (hts *headerTipsStore) IsStaged() bool {
return hts.staging != nil
}
func (h *headerTipsStore) Tips(dbContext model.DBReader) ([]*externalapi.DomainHash, error) {
if h.staging != nil {
return h.staging, nil
func (hts *headerTipsStore) Tips(dbContext model.DBReader) ([]*externalapi.DomainHash, error) {
if hts.staging != nil {
return externalapi.CloneHashes(hts.staging), nil
}
if hts.cache != nil {
return externalapi.CloneHashes(hts.cache), nil
}
tipsBytes, err := dbContext.Get(headerTipsKey)
@@ -63,25 +77,25 @@ func (h *headerTipsStore) Tips(dbContext model.DBReader) ([]*externalapi.DomainH
return nil, err
}
return h.deserializeTips(tipsBytes)
tips, err := hts.deserializeTips(tipsBytes)
if err != nil {
return nil, err
}
hts.cache = tips
return externalapi.CloneHashes(tips), nil
}
func (h *headerTipsStore) serializeTips(tips []*externalapi.DomainHash) ([]byte, error) {
func (hts *headerTipsStore) serializeTips(tips []*externalapi.DomainHash) ([]byte, error) {
dbTips := serialization.HeaderTipsToDBHeaderTips(tips)
return proto.Marshal(dbTips)
}
func (h *headerTipsStore) deserializeTips(tipsBytes []byte) ([]*externalapi.DomainHash, error) {
func (hts *headerTipsStore) deserializeTips(tipsBytes []byte) ([]*externalapi.DomainHash, error) {
dbTips := &serialization.DbHeaderTips{}
err := proto.Unmarshal(tipsBytes, dbTips)
if err != nil {
return nil, err
}
return serialization.DBHeaderTipsTOHeaderTips(dbTips)
}
// New instantiates a new HeaderTipsStore
func New() model.HeaderTipsStore {
return &headerTipsStore{}
return serialization.DBHeaderTipsToHeaderTips(dbTips)
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
)
var bucket = dbkeys.MakeBucket([]byte("multisets"))
@@ -14,19 +15,21 @@ var bucket = dbkeys.MakeBucket([]byte("multisets"))
type multisetStore struct {
staging map[externalapi.DomainHash]model.Multiset
toDelete map[externalapi.DomainHash]struct{}
cache *lrucache.LRUCache
}
// New instantiates a new MultisetStore
func New() model.MultisetStore {
func New(cacheSize int) model.MultisetStore {
return &multisetStore{
staging: make(map[externalapi.DomainHash]model.Multiset),
toDelete: make(map[externalapi.DomainHash]struct{}),
cache: lrucache.New(cacheSize),
}
}
// Stage stages the given multiset for the given blockHash
func (ms *multisetStore) Stage(blockHash *externalapi.DomainHash, multiset model.Multiset) {
ms.staging[*blockHash] = multiset
ms.staging[*blockHash] = multiset.Clone()
}
func (ms *multisetStore) IsStaged() bool {
@@ -44,11 +47,11 @@ func (ms *multisetStore) Commit(dbTx model.DBTransaction) error {
if err != nil {
return err
}
err = dbTx.Put(ms.hashAsKey(&hash), multisetBytes)
if err != nil {
return err
}
ms.cache.Add(&hash, multiset)
}
for hash := range ms.toDelete {
@@ -56,6 +59,7 @@ func (ms *multisetStore) Commit(dbTx model.DBTransaction) error {
if err != nil {
return err
}
ms.cache.Remove(&hash)
}
ms.Discard()
@@ -65,7 +69,11 @@ func (ms *multisetStore) Commit(dbTx model.DBTransaction) error {
// Get gets the multiset associated with the given blockHash
func (ms *multisetStore) Get(dbContext model.DBReader, blockHash *externalapi.DomainHash) (model.Multiset, error) {
if multiset, ok := ms.staging[*blockHash]; ok {
return multiset, nil
return multiset.Clone(), nil
}
if multiset, ok := ms.cache.Get(blockHash); ok {
return multiset.(model.Multiset).Clone(), nil
}
multisetBytes, err := dbContext.Get(ms.hashAsKey(blockHash))
@@ -73,7 +81,12 @@ func (ms *multisetStore) Get(dbContext model.DBReader, blockHash *externalapi.Do
return nil, err
}
return ms.deserializeMultiset(multisetBytes)
multiset, err := ms.deserializeMultiset(multisetBytes)
if err != nil {
return nil, err
}
ms.cache.Add(blockHash, multiset)
return multiset.Clone(), nil
}
// Delete deletes the multiset associated with the given blockHash

View File

@@ -15,19 +15,17 @@ var pruningSerializedUTXOSetkey = dbkeys.MakeBucket().Key([]byte("pruning-utxo-s
type pruningStore struct {
pruningPointStaging *externalapi.DomainHash
serializedUTXOSetStaging []byte
pruningPointCache *externalapi.DomainHash
}
// New instantiates a new PruningStore
func New() model.PruningStore {
return &pruningStore{
pruningPointStaging: nil,
serializedUTXOSetStaging: nil,
}
return &pruningStore{}
}
// Stage stages the pruning state
func (ps *pruningStore) Stage(pruningPointBlockHash *externalapi.DomainHash, pruningPointUTXOSetBytes []byte) {
ps.pruningPointStaging = pruningPointBlockHash
ps.pruningPointStaging = pruningPointBlockHash.Clone()
ps.serializedUTXOSetStaging = pruningPointUTXOSetBytes
}
@@ -50,6 +48,7 @@ func (ps *pruningStore) Commit(dbTx model.DBTransaction) error {
if err != nil {
return err
}
ps.pruningPointCache = ps.pruningPointStaging
}
if ps.serializedUTXOSetStaging != nil {
@@ -57,7 +56,6 @@ func (ps *pruningStore) Commit(dbTx model.DBTransaction) error {
if err != nil {
return err
}
err = dbTx.Put(pruningSerializedUTXOSetkey, utxoSetBytes)
if err != nil {
return err
@@ -74,16 +72,21 @@ func (ps *pruningStore) PruningPoint(dbContext model.DBReader) (*externalapi.Dom
return ps.pruningPointStaging, nil
}
blockHashBytes, err := dbContext.Get(pruningBlockHashKey)
if ps.pruningPointCache != nil {
return ps.pruningPointCache, nil
}
pruningPointBytes, err := dbContext.Get(pruningBlockHashKey)
if err != nil {
return nil, err
}
blockHash, err := ps.deserializePruningPoint(blockHashBytes)
pruningPoint, err := ps.deserializePruningPoint(pruningPointBytes)
if err != nil {
return nil, err
}
return blockHash, nil
ps.pruningPointCache = pruningPoint
return pruningPoint, nil
}
// PruningPointSerializedUTXOSet returns the serialized UTXO set of the current pruning point
@@ -91,7 +94,17 @@ func (ps *pruningStore) PruningPointSerializedUTXOSet(dbContext model.DBReader)
if ps.serializedUTXOSetStaging != nil {
return ps.serializedUTXOSetStaging, nil
}
return dbContext.Get(pruningSerializedUTXOSetkey)
dbPruningPointUTXOSetBytes, err := dbContext.Get(pruningSerializedUTXOSetkey)
if err != nil {
return nil, err
}
pruningPointUTXOSet, err := ps.deserializeUTXOSetBytes(dbPruningPointUTXOSetBytes)
if err != nil {
return nil, err
}
return pruningPointUTXOSet, nil
}
func (ps *pruningStore) serializePruningPoint(pruningPoint *externalapi.DomainHash) ([]byte, error) {
@@ -127,5 +140,9 @@ func (ps *pruningStore) HasPruningPoint(dbContext model.DBReader) (bool, error)
return true, nil
}
if ps.pruningPointCache != nil {
return true, nil
}
return dbContext.Has(pruningBlockHashKey)
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
)
var reachabilityDataBucket = dbkeys.MakeBucket([]byte("reachability-data"))
@@ -15,19 +16,22 @@ var reachabilityReindexRootKey = dbkeys.MakeBucket().Key([]byte("reachability-re
type reachabilityDataStore struct {
reachabilityDataStaging map[externalapi.DomainHash]*model.ReachabilityData
reachabilityReindexRootStaging *externalapi.DomainHash
reachabilityDataCache *lrucache.LRUCache
reachabilityReindexRootCache *externalapi.DomainHash
}
// New instantiates a new ReachabilityDataStore
func New() model.ReachabilityDataStore {
func New(cacheSize int) model.ReachabilityDataStore {
return &reachabilityDataStore{
reachabilityDataStaging: make(map[externalapi.DomainHash]*model.ReachabilityData),
reachabilityReindexRootStaging: nil,
reachabilityDataStaging: make(map[externalapi.DomainHash]*model.ReachabilityData),
reachabilityDataCache: lrucache.New(cacheSize),
}
}
// StageReachabilityData stages the given reachabilityData for the given blockHash
func (rds *reachabilityDataStore) StageReachabilityData(blockHash *externalapi.DomainHash, reachabilityData *model.ReachabilityData) {
rds.reachabilityDataStaging[*blockHash] = reachabilityData
func (rds *reachabilityDataStore) StageReachabilityData(blockHash *externalapi.DomainHash,
reachabilityData *model.ReachabilityData) {
rds.reachabilityDataStaging[*blockHash] = reachabilityData.Clone()
}
// StageReachabilityReindexRoot stages the given reachabilityReindexRoot
@@ -50,22 +54,22 @@ func (rds *reachabilityDataStore) Commit(dbTx model.DBTransaction) error {
if err != nil {
return err
}
err = dbTx.Put(reachabilityReindexRootKey, reachabilityReindexRootBytes)
if err != nil {
return err
}
rds.reachabilityReindexRootCache = rds.reachabilityReindexRootStaging
}
for hash, reachabilityData := range rds.reachabilityDataStaging {
reachabilityDataBytes, err := rds.serializeReachabilityData(reachabilityData)
if err != nil {
return err
}
err = dbTx.Put(rds.reachabilityDataBlockHashAsKey(&hash), reachabilityDataBytes)
if err != nil {
return err
}
rds.reachabilityDataCache.Add(&hash, reachabilityData)
}
rds.Discard()
@@ -77,7 +81,11 @@ func (rds *reachabilityDataStore) ReachabilityData(dbContext model.DBReader,
blockHash *externalapi.DomainHash) (*model.ReachabilityData, error) {
if reachabilityData, ok := rds.reachabilityDataStaging[*blockHash]; ok {
return reachabilityData, nil
return reachabilityData.Clone(), nil
}
if reachabilityData, ok := rds.reachabilityDataCache.Get(blockHash); ok {
return reachabilityData.(*model.ReachabilityData).Clone(), nil
}
reachabilityDataBytes, err := dbContext.Get(rds.reachabilityDataBlockHashAsKey(blockHash))
@@ -85,7 +93,12 @@ func (rds *reachabilityDataStore) ReachabilityData(dbContext model.DBReader,
return nil, err
}
return rds.deserializeReachabilityData(reachabilityDataBytes)
reachabilityData, err := rds.deserializeReachabilityData(reachabilityDataBytes)
if err != nil {
return nil, err
}
rds.reachabilityDataCache.Add(blockHash, reachabilityData)
return reachabilityData.Clone(), nil
}
func (rds *reachabilityDataStore) HasReachabilityData(dbContext model.DBReader, blockHash *externalapi.DomainHash) (bool, error) {
@@ -93,6 +106,10 @@ func (rds *reachabilityDataStore) HasReachabilityData(dbContext model.DBReader,
return true, nil
}
if rds.reachabilityDataCache.Has(blockHash) {
return true, nil
}
return dbContext.Has(rds.reachabilityDataBlockHashAsKey(blockHash))
}
@@ -101,6 +118,11 @@ func (rds *reachabilityDataStore) ReachabilityReindexRoot(dbContext model.DBRead
if rds.reachabilityReindexRootStaging != nil {
return rds.reachabilityReindexRootStaging, nil
}
if rds.reachabilityReindexRootCache != nil {
return rds.reachabilityReindexRootCache, nil
}
reachabilityReindexRootBytes, err := dbContext.Get(reachabilityReindexRootKey)
if err != nil {
return nil, err
@@ -110,6 +132,7 @@ func (rds *reachabilityDataStore) ReachabilityReindexRoot(dbContext model.DBRead
if err != nil {
return nil, err
}
rds.reachabilityReindexRootCache = reachabilityReindexRoot
return reachabilityReindexRoot, nil
}

View File

@@ -6,6 +6,8 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
"github.com/pkg/errors"
)
var utxoDiffBucket = dbkeys.MakeBucket([]byte("utxo-diffs"))
@@ -16,21 +18,28 @@ type utxoDiffStore struct {
utxoDiffStaging map[externalapi.DomainHash]*model.UTXODiff
utxoDiffChildStaging map[externalapi.DomainHash]*externalapi.DomainHash
toDelete map[externalapi.DomainHash]struct{}
utxoDiffCache *lrucache.LRUCache
utxoDiffChildCache *lrucache.LRUCache
}
// New instantiates a new UTXODiffStore
func New() model.UTXODiffStore {
func New(cacheSize int) model.UTXODiffStore {
return &utxoDiffStore{
utxoDiffStaging: make(map[externalapi.DomainHash]*model.UTXODiff),
utxoDiffChildStaging: make(map[externalapi.DomainHash]*externalapi.DomainHash),
toDelete: make(map[externalapi.DomainHash]struct{}),
utxoDiffCache: lrucache.New(cacheSize),
utxoDiffChildCache: lrucache.New(cacheSize),
}
}
// Stage stages the given utxoDiff for the given blockHash
func (uds *utxoDiffStore) Stage(blockHash *externalapi.DomainHash, utxoDiff *model.UTXODiff, utxoDiffChild *externalapi.DomainHash) {
uds.utxoDiffStaging[*blockHash] = utxoDiff
uds.utxoDiffChildStaging[*blockHash] = utxoDiffChild
uds.utxoDiffStaging[*blockHash] = utxoDiff.Clone()
if utxoDiffChild != nil {
uds.utxoDiffChildStaging[*blockHash] = utxoDiffChild.Clone()
}
}
func (uds *utxoDiffStore) IsStaged() bool {
@@ -57,22 +66,26 @@ func (uds *utxoDiffStore) Commit(dbTx model.DBTransaction) error {
if err != nil {
return err
}
err = dbTx.Put(uds.utxoDiffHashAsKey(&hash), utxoDiffBytes)
if err != nil {
return err
}
uds.utxoDiffCache.Add(&hash, utxoDiff)
}
for hash, utxoDiffChild := range uds.utxoDiffChildStaging {
if utxoDiffChild == nil {
continue
}
utxoDiffChildBytes, err := uds.serializeUTXODiffChild(utxoDiffChild)
if err != nil {
return err
}
err = dbTx.Put(uds.utxoDiffHashAsKey(&hash), utxoDiffChildBytes)
err = dbTx.Put(uds.utxoDiffChildHashAsKey(&hash), utxoDiffChildBytes)
if err != nil {
return err
}
uds.utxoDiffChildCache.Add(&hash, utxoDiffChild)
}
for hash := range uds.toDelete {
@@ -80,11 +93,13 @@ func (uds *utxoDiffStore) Commit(dbTx model.DBTransaction) error {
if err != nil {
return err
}
uds.utxoDiffCache.Remove(&hash)
err = dbTx.Delete(uds.utxoDiffChildHashAsKey(&hash))
if err != nil {
return err
}
uds.utxoDiffChildCache.Remove(&hash)
}
uds.Discard()
@@ -94,7 +109,11 @@ func (uds *utxoDiffStore) Commit(dbTx model.DBTransaction) error {
// UTXODiff gets the utxoDiff associated with the given blockHash
func (uds *utxoDiffStore) UTXODiff(dbContext model.DBReader, blockHash *externalapi.DomainHash) (*model.UTXODiff, error) {
if utxoDiff, ok := uds.utxoDiffStaging[*blockHash]; ok {
return utxoDiff, nil
return utxoDiff.Clone(), nil
}
if utxoDiff, ok := uds.utxoDiffCache.Get(blockHash); ok {
return utxoDiff.(*model.UTXODiff).Clone(), nil
}
utxoDiffBytes, err := dbContext.Get(uds.utxoDiffHashAsKey(blockHash))
@@ -102,13 +121,22 @@ func (uds *utxoDiffStore) UTXODiff(dbContext model.DBReader, blockHash *external
return nil, err
}
return uds.deserializeUTXODiff(utxoDiffBytes)
utxoDiff, err := uds.deserializeUTXODiff(utxoDiffBytes)
if err != nil {
return nil, err
}
uds.utxoDiffCache.Add(blockHash, utxoDiff)
return utxoDiff.Clone(), nil
}
// UTXODiffChild gets the utxoDiff child associated with the given blockHash
func (uds *utxoDiffStore) UTXODiffChild(dbContext model.DBReader, blockHash *externalapi.DomainHash) (*externalapi.DomainHash, error) {
if utxoDiffChild, ok := uds.utxoDiffChildStaging[*blockHash]; ok {
return utxoDiffChild, nil
return utxoDiffChild.Clone(), nil
}
if utxoDiffChild, ok := uds.utxoDiffChildCache.Get(blockHash); ok {
return utxoDiffChild.(*externalapi.DomainHash).Clone(), nil
}
utxoDiffChildBytes, err := dbContext.Get(uds.utxoDiffChildHashAsKey(blockHash))
@@ -120,7 +148,8 @@ func (uds *utxoDiffStore) UTXODiffChild(dbContext model.DBReader, blockHash *ext
if err != nil {
return nil, err
}
return utxoDiffChild, nil
uds.utxoDiffChildCache.Add(blockHash, utxoDiffChild)
return utxoDiffChild.Clone(), nil
}
// HasUTXODiffChild returns true if the given blockHash has a UTXODiffChild
@@ -129,6 +158,10 @@ func (uds *utxoDiffStore) HasUTXODiffChild(dbContext model.DBReader, blockHash *
return true, nil
}
if uds.utxoDiffChildCache.Has(blockHash) {
return true, nil
}
return dbContext.Has(uds.utxoDiffChildHashAsKey(blockHash))
}
@@ -155,28 +188,37 @@ func (uds *utxoDiffStore) utxoDiffChildHashAsKey(hash *externalapi.DomainHash) m
}
func (uds *utxoDiffStore) serializeUTXODiff(utxoDiff *model.UTXODiff) ([]byte, error) {
return proto.Marshal(serialization.UTXODiffToDBUTXODiff(utxoDiff))
dbUtxoDiff := serialization.UTXODiffToDBUTXODiff(utxoDiff)
bytes, err := proto.Marshal(dbUtxoDiff)
if err != nil {
return nil, errors.WithStack(err)
}
return bytes, nil
}
func (uds *utxoDiffStore) deserializeUTXODiff(utxoDiffBytes []byte) (*model.UTXODiff, error) {
dbUTXODiff := &serialization.DbUtxoDiff{}
err := proto.Unmarshal(utxoDiffBytes, dbUTXODiff)
if err != nil {
return nil, err
return nil, errors.WithStack(err)
}
return serialization.DBUTXODiffToUTXODiff(dbUTXODiff)
}
func (uds *utxoDiffStore) serializeUTXODiffChild(utxoDiffChild *externalapi.DomainHash) ([]byte, error) {
return proto.Marshal(serialization.DomainHashToDbHash(utxoDiffChild))
bytes, err := proto.Marshal(serialization.DomainHashToDbHash(utxoDiffChild))
if err != nil {
return nil, errors.WithStack(err)
}
return bytes, nil
}
func (uds *utxoDiffStore) deserializeUTXODiffChild(utxoDiffChildBytes []byte) (*externalapi.DomainHash, error) {
dbHash := &serialization.DbHash{}
err := proto.Unmarshal(utxoDiffChildBytes, dbHash)
if err != nil {
return nil, err
return nil, errors.WithStack(err)
}
return serialization.DbHashToDomainHash(dbHash)

View File

@@ -1,6 +1,14 @@
package consensus
import (
"io/ioutil"
"os"
"sync"
"github.com/kaspanet/kaspad/domain/consensus/processes/dagtraversalmanager"
"github.com/kaspanet/kaspad/infrastructure/db/database/ldb"
consensusdatabase "github.com/kaspanet/kaspad/domain/consensus/database"
"github.com/kaspanet/kaspad/domain/consensus/datastructures/acceptancedatastore"
"github.com/kaspanet/kaspad/domain/consensus/datastructures/blockheaderstore"
@@ -16,12 +24,13 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/datastructures/utxodiffstore"
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
"github.com/kaspanet/kaspad/domain/consensus/processes/blockbuilder"
"github.com/kaspanet/kaspad/domain/consensus/processes/blockprocessor"
"github.com/kaspanet/kaspad/domain/consensus/processes/blockvalidator"
"github.com/kaspanet/kaspad/domain/consensus/processes/coinbasemanager"
"github.com/kaspanet/kaspad/domain/consensus/processes/consensusstatemanager"
"github.com/kaspanet/kaspad/domain/consensus/processes/dagtopologymanager"
"github.com/kaspanet/kaspad/domain/consensus/processes/dagtraversalmanager"
"github.com/kaspanet/kaspad/domain/consensus/processes/difficultymanager"
"github.com/kaspanet/kaspad/domain/consensus/processes/ghostdagmanager"
"github.com/kaspanet/kaspad/domain/consensus/processes/headertipsmanager"
@@ -37,34 +46,48 @@ import (
// Factory instantiates new Consensuses
type Factory interface {
NewConsensus(dagParams *dagconfig.Params, db infrastructuredatabase.Database) (Consensus, error)
NewConsensus(dagParams *dagconfig.Params, db infrastructuredatabase.Database) (externalapi.Consensus, error)
NewTestConsensus(dagParams *dagconfig.Params, testName string) (tc testapi.TestConsensus, teardown func(), err error)
NewTestConsensusWithDataDir(dagParams *dagconfig.Params, dataDir string) (
tc testapi.TestConsensus, teardown func(), err error)
}
type factory struct{}
// NewConsensus instantiates a new Consensus
func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredatabase.Database) (Consensus, error) {
// Data Structures
acceptanceDataStore := acceptancedatastore.New()
blockStore := blockstore.New()
blockHeaderStore := blockheaderstore.New()
blockRelationStore := blockrelationstore.New()
blockStatusStore := blockstatusstore.New()
multisetStore := multisetstore.New()
pruningStore := pruningstore.New()
reachabilityDataStore := reachabilitydatastore.New()
utxoDiffStore := utxodiffstore.New()
consensusStateStore := consensusstatestore.New()
ghostdagDataStore := ghostdagdatastore.New()
headerTipsStore := headertipsstore.New()
// NewFactory creates a new Consensus factory
func NewFactory() Factory {
return &factory{}
}
// NewConsensus instantiates a new Consensus
func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredatabase.Database) (externalapi.Consensus, error) {
dbManager := consensusdatabase.New(db)
// Data Structures
storeCacheSize := 200
acceptanceDataStore := acceptancedatastore.New(storeCacheSize)
blockStore, err := blockstore.New(dbManager, storeCacheSize)
if err != nil {
return nil, err
}
blockHeaderStore, err := blockheaderstore.New(dbManager, storeCacheSize)
if err != nil {
return nil, err
}
blockRelationStore := blockrelationstore.New(storeCacheSize)
blockStatusStore := blockstatusstore.New(storeCacheSize)
multisetStore := multisetstore.New(storeCacheSize)
pruningStore := pruningstore.New()
reachabilityDataStore := reachabilitydatastore.New(storeCacheSize)
utxoDiffStore := utxodiffstore.New(storeCacheSize)
consensusStateStore := consensusstatestore.New()
ghostdagDataStore := ghostdagdatastore.New(storeCacheSize)
headerTipsStore := headertipsstore.New()
// Processes
reachabilityManager := reachabilitymanager.New(
dbManager,
ghostdagDataStore,
blockRelationStore,
reachabilityDataStore)
dagTopologyManager := dagtopologymanager.New(
dbManager,
@@ -84,8 +107,10 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
dagParams.TimestampDeviationTolerance,
dbManager,
dagTraversalManager,
blockHeaderStore)
blockHeaderStore,
ghostdagDataStore)
transactionValidator := transactionvalidator.New(dagParams.BlockCoinbaseMaturity,
dagParams.EnableNonNativeSubnetworks,
dbManager,
pastMedianTimeManager,
ghostdagDataStore)
@@ -104,7 +129,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
ghostdagDataStore,
acceptanceDataStore)
headerTipsManager := headertipsmanager.New(dbManager, dagTopologyManager, ghostdagManager, headerTipsStore)
genesisHash := (*externalapi.DomainHash)(dagParams.GenesisHash)
genesisHash := dagParams.GenesisHash
mergeDepthManager := mergedepthmanager.New(
dagParams.FinalityDepth(),
dbManager,
@@ -113,7 +138,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
ghostdagDataStore)
blockValidator := blockvalidator.New(
dagParams.PowMax,
false,
dagParams.SkipProofOfWork,
genesisHash,
dagParams.EnableNonNativeSubnetworks,
dagParams.DisableDifficultyAdjustment,
@@ -138,6 +163,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
dbManager,
dagParams.FinalityDepth(),
dagParams.PruningDepth(),
genesisHash,
ghostdagManager,
dagTopologyManager,
dagTraversalManager,
@@ -191,7 +217,21 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
ghostdagDataStore,
blockStatusStore,
blockHeaderStore,
headerTipsStore)
headerTipsStore,
blockStore)
blockBuilder := blockbuilder.New(
dbManager,
difficultyManager,
pastMedianTimeManager,
coinbaseManager,
consensusStateManager,
ghostdagManager,
acceptanceDataStore,
blockRelationStore,
multisetStore,
ghostdagDataStore,
)
blockProcessor := blockprocessor.New(
dagParams,
@@ -222,19 +262,38 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
headerTipsStore)
c := &consensus{
lock: &sync.Mutex{},
databaseContext: dbManager,
blockProcessor: blockProcessor,
blockBuilder: blockBuilder,
consensusStateManager: consensusStateManager,
transactionValidator: transactionValidator,
syncManager: syncManager,
pastMedianTimeManager: pastMedianTimeManager,
blockValidator: blockValidator,
coinbaseManager: coinbaseManager,
dagTopologyManager: dagTopologyManager,
dagTraversalManager: dagTraversalManager,
difficultyManager: difficultyManager,
ghostdagManager: ghostdagManager,
headerTipsManager: headerTipsManager,
mergeDepthManager: mergeDepthManager,
pruningManager: pruningManager,
reachabilityManager: reachabilityManager,
blockStore: blockStore,
blockHeaderStore: blockHeaderStore,
pruningStore: pruningStore,
ghostdagDataStore: ghostdagDataStore,
blockStatusStore: blockStatusStore,
acceptanceDataStore: acceptanceDataStore,
blockStore: blockStore,
blockHeaderStore: blockHeaderStore,
pruningStore: pruningStore,
ghostdagDataStore: ghostdagDataStore,
blockStatusStore: blockStatusStore,
blockRelationStore: blockRelationStore,
consensusStateStore: consensusStateStore,
headerTipsStore: headerTipsStore,
multisetStore: multisetStore,
reachabilityDataStore: reachabilityDataStore,
utxoDiffStore: utxoDiffStore,
}
genesisInfo, err := c.GetBlockInfo(genesisHash)
@@ -252,7 +311,44 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
return c, nil
}
// NewFactory creates a new Consensus factory
func NewFactory() Factory {
return &factory{}
func (f *factory) NewTestConsensus(dagParams *dagconfig.Params, testName string) (
tc testapi.TestConsensus, teardown func(), err error) {
dataDir, err := ioutil.TempDir("", testName)
if err != nil {
return nil, nil, err
}
return f.NewTestConsensusWithDataDir(dagParams, dataDir)
}
func (f *factory) NewTestConsensusWithDataDir(dagParams *dagconfig.Params, dataDir string) (
tc testapi.TestConsensus, teardown func(), err error) {
db, err := ldb.NewLevelDB(dataDir)
if err != nil {
return nil, nil, err
}
consensusAsInterface, err := f.NewConsensus(dagParams, db)
if err != nil {
return nil, nil, err
}
consensusAsImplementation := consensusAsInterface.(*consensus)
testConsensusStateManager := consensusstatemanager.NewTestConsensusStateManager(consensusAsImplementation.consensusStateManager)
tstConsensus := &testConsensus{
consensus: consensusAsImplementation,
testConsensusStateManager: testConsensusStateManager,
testReachabilityManager: reachabilitymanager.NewTestReachabilityManager(consensusAsImplementation.
reachabilityManager),
}
tstConsensus.testBlockBuilder = blockbuilder.NewTestBlockBuilder(consensusAsImplementation.blockBuilder, tstConsensus)
teardown = func() {
db.Close()
os.RemoveAll(dataDir)
}
return tstConsensus, teardown, nil
}

View File

@@ -0,0 +1,420 @@
package consensus
import (
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/pkg/errors"
"fmt"
"testing"
)
func TestFinality(t *testing.T) {
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
// Set finalityInterval to 50 blocks, so that test runs quickly
params.FinalityDuration = 50 * params.TargetTimePerBlock
factory := NewFactory()
consensus, teardown, err := factory.NewTestConsensus(params, "TestFinality")
if err != nil {
t.Fatalf("Error setting up consensus: %+v", err)
}
defer teardown()
buildAndInsertBlock := func(parentHashes []*externalapi.DomainHash) (*externalapi.DomainBlock, error) {
block, _, err := consensus.BuildBlockWithParents(parentHashes, nil, nil)
if err != nil {
return nil, err
}
err = consensus.ValidateAndInsertBlock(block)
if err != nil {
return nil, err
}
return block, nil
}
// Build a chain of `finalityInterval - 1` blocks
finalityInterval := params.FinalityDepth()
var mainChainTip *externalapi.DomainBlock
mainChainTipHash := params.GenesisHash
for i := uint64(0); i < finalityInterval-1; i++ {
mainChainTip, err = buildAndInsertBlock([]*externalapi.DomainHash{mainChainTipHash})
if err != nil {
t.Fatalf("TestFinality: Failed to process Block #%d: %v", i, err)
}
mainChainTipHash = consensusserialization.BlockHash(mainChainTip)
blockInfo, err := consensus.GetBlockInfo(mainChainTipHash)
if err != nil {
t.Fatalf("TestFinality: Block #%d failed to get info: %v", i, err)
}
if blockInfo.BlockStatus != externalapi.StatusValid {
t.Fatalf("Block #%d in main chain expected to have status '%s', but got '%s'",
i, externalapi.StatusValid, blockInfo.BlockStatus)
}
}
// Mine another chain of `finality-Interval - 2` blocks
var sideChainTip *externalapi.DomainBlock
sideChainTipHash := params.GenesisHash
for i := uint64(0); i < finalityInterval-2; i++ {
sideChainTip, err = buildAndInsertBlock([]*externalapi.DomainHash{sideChainTipHash})
if err != nil {
t.Fatalf("TestFinality: Failed to process sidechain Block #%d: %v", i, err)
}
sideChainTipHash = consensusserialization.BlockHash(sideChainTip)
blockInfo, err := consensus.GetBlockInfo(sideChainTipHash)
if err != nil {
t.Fatalf("TestFinality: Block #%d failed to get info: %v", i, err)
} else if !blockInfo.Exists {
t.Fatalf("TestFinality: Failed getting block info, doesn't exists")
}
if blockInfo.BlockStatus != externalapi.StatusUTXOPendingVerification {
t.Fatalf("Block #%d in side chain expected to have status '%s', but got '%s'",
i, externalapi.StatusUTXOPendingVerification, blockInfo.BlockStatus)
}
}
// Add two more blocks in the side-chain until it becomes the selected chain
for i := uint64(0); i < 2; i++ {
sideChainTip, err = buildAndInsertBlock([]*externalapi.DomainHash{sideChainTipHash})
if err != nil {
t.Fatalf("TestFinality: Failed to process sidechain Block #%d: %v", i, err)
}
sideChainTipHash = consensusserialization.BlockHash(sideChainTip)
}
// Make sure that now the sideChainTip is valid and selectedTip
blockInfo, err := consensus.GetBlockInfo(sideChainTipHash)
if err != nil {
t.Fatalf("TestFinality: Failed to get block info: %v", err)
} else if !blockInfo.Exists {
t.Fatalf("TestFinality: Failed getting block info, doesn't exists")
}
if blockInfo.BlockStatus != externalapi.StatusValid {
t.Fatalf("TestFinality: Overtaking block in side-chain expected to have status '%s', but got '%s'",
externalapi.StatusValid, blockInfo.BlockStatus)
}
selectedTip, err := consensus.GetVirtualSelectedParent()
if err != nil {
t.Fatalf("TestFinality: Failed getting virtual selectedParent: %v", err)
}
if *consensusserialization.BlockHash(selectedTip) != *sideChainTipHash {
t.Fatalf("Overtaking block in side-chain is not selectedTip")
}
// Add two more blocks to main chain, to move finality point to first non-genesis block in mainChain
for i := uint64(0); i < 2; i++ {
mainChainTip, err = buildAndInsertBlock([]*externalapi.DomainHash{mainChainTipHash})
if err != nil {
t.Fatalf("TestFinality: Failed to process sidechain Block #%d: %v", i, err)
}
mainChainTipHash = consensusserialization.BlockHash(mainChainTip)
}
virtualFinality, err := consensus.ConsensusStateManager().VirtualFinalityPoint()
if err != nil {
t.Fatalf("TestFinality: Failed getting the virtual's finality point: %v", err)
}
if *virtualFinality == *params.GenesisHash {
t.Fatalf("virtual's finalityPoint is still genesis after adding finalityInterval + 1 blocks to the main chain")
}
// TODO: Make sure that a finality conflict notification is sent
// Add two more blocks to the side chain, so that it violates finality and gets status UTXOPendingVerification even
// though it is the block with the highest blue score.
for i := uint64(0); i < 2; i++ {
sideChainTip, err = buildAndInsertBlock([]*externalapi.DomainHash{sideChainTipHash})
if err != nil {
t.Fatalf("TestFinality: Failed to process sidechain Block #%d: %v", i, err)
}
sideChainTipHash = consensusserialization.BlockHash(sideChainTip)
}
// Check that sideChainTip hash higher blue score than the selected parent
selectedTip, err = consensus.GetVirtualSelectedParent()
if err != nil {
t.Fatalf("TestFinality: Failed getting virtual selectedParent: %v", err)
}
selectedTipGhostDagData, err := consensus.GHOSTDAGDataStore().Get(consensus.DatabaseContext(), consensusserialization.BlockHash(selectedTip))
if err != nil {
t.Fatalf("TestFinality: Failed getting the ghost dag data of the selected tip: %v", err)
}
sideChainTipGhostDagData, err := consensus.GHOSTDAGDataStore().Get(consensus.DatabaseContext(), sideChainTipHash)
if err != nil {
t.Fatalf("TestFinality: Failed getting the ghost dag data of the sidechain tip: %v", err)
}
if selectedTipGhostDagData.BlueScore > sideChainTipGhostDagData.BlueScore {
t.Fatalf("sideChainTip is not the bluest tip when it is expected to be")
}
// Blocks violating finality should have a UTXOPendingVerification status
blockInfo, err = consensus.GetBlockInfo(sideChainTipHash)
if err != nil {
t.Fatalf("TestFinality: Failed to get block info: %v", err)
} else if !blockInfo.Exists {
t.Fatalf("TestFinality: Failed getting block info, doesn't exists")
}
if blockInfo.BlockStatus != externalapi.StatusUTXOPendingVerification {
t.Fatalf("TestFinality: Finality violating block expected to have status '%s', but got '%s'",
externalapi.StatusUTXOPendingVerification, blockInfo.BlockStatus)
}
})
}
func TestBoundedMergeDepth(t *testing.T) {
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
// Set finalityInterval to 50 blocks, so that test runs quickly
params.FinalityDuration = 50 * params.TargetTimePerBlock
finalityInterval := int(params.FinalityDepth())
if int(params.K) >= finalityInterval {
t.Fatal("K must be smaller than finality duration for this test to run")
}
checkViolatingMergeDepth := func(consensus testapi.TestConsensus, parents []*externalapi.DomainHash) (*externalapi.DomainBlock, bool) {
block, _, err := consensus.BuildBlockWithParents(parents, nil, nil)
if err != nil {
t.Fatalf("TestBoundedMergeDepth: BuildBlockWithParents failed: %v", err)
return nil, false // fo some reason go doesn't recognize that t.Fatalf never returns
}
err = consensus.ValidateAndInsertBlock(block)
if err == nil {
return block, false
} else if errors.Is(err, ruleerrors.ErrViolatingBoundedMergeDepth) {
return block, true
} else {
t.Fatalf("TestBoundedMergeDepth: expected err: %v, found err: %v", ruleerrors.ErrViolatingBoundedMergeDepth, err)
return nil, false // fo some reason go doesn't recognize that t.Fatalf never returns
}
}
processBlock := func(consensus testapi.TestConsensus, block *externalapi.DomainBlock, name string) {
err := consensus.ValidateAndInsertBlock(block)
if err != nil {
t.Fatalf("TestBoundedMergeDepth: %s got unexpected error from ProcessBlock: %+v", name, err)
}
}
buildAndInsertBlock := func(consensus testapi.TestConsensus, parentHashes []*externalapi.DomainHash) *externalapi.DomainBlock {
block, _, err := consensus.BuildBlockWithParents(parentHashes, nil, nil)
if err != nil {
t.Fatalf("TestBoundedMergeDepth: Failed building block: %v", err)
}
err = consensus.ValidateAndInsertBlock(block)
if err != nil {
t.Fatalf("TestBoundedMergeDepth: Failed Inserting block to consensus: %v", err)
}
return block
}
getStatus := func(consensus testapi.TestConsensus, block *externalapi.DomainBlock) externalapi.BlockStatus {
blockInfo, err := consensus.GetBlockInfo(consensusserialization.BlockHash(block))
if err != nil {
t.Fatalf("TestBoundedMergeDepth: Failed to get block info: %v", err)
} else if !blockInfo.Exists {
t.Fatalf("TestBoundedMergeDepth: Failed to get block info, block doesn't exists")
}
return blockInfo.BlockStatus
}
factory := NewFactory()
consensusBuild, teardownFunc1, err := factory.NewTestConsensus(params, "BoundedMergeTestBuild")
if err != nil {
t.Fatalf("TestBoundedMergeDepth: Error setting up consensus: %+v", err)
}
consensusReal, teardownFunc2, err := factory.NewTestConsensus(params, "BoundedMergeTestReal")
if err != nil {
t.Fatalf("TestBoundedMergeDepth: Error setting up consensus: %+v", err)
}
defer teardownFunc2()
// Create a block on top on genesis
block1 := buildAndInsertBlock(consensusBuild, []*externalapi.DomainHash{params.GenesisHash})
// Create a chain
selectedChain := make([]*externalapi.DomainBlock, 0, finalityInterval+1)
parent := consensusserialization.BlockHash(block1)
// Make sure this is always bigger than `blocksChain2` so it will stay the selected chain
for i := 0; i < finalityInterval+2; i++ {
block := buildAndInsertBlock(consensusBuild, []*externalapi.DomainHash{parent})
selectedChain = append(selectedChain, block)
parent = consensusserialization.BlockHash(block)
}
// Create another chain
blocksChain2 := make([]*externalapi.DomainBlock, 0, finalityInterval+1)
parent = consensusserialization.BlockHash(block1)
for i := 0; i < finalityInterval+1; i++ {
block := buildAndInsertBlock(consensusBuild, []*externalapi.DomainHash{parent})
blocksChain2 = append(blocksChain2, block)
parent = consensusserialization.BlockHash(block)
}
// Teardown and assign nil to make sure we use the right DAG from here on.
teardownFunc1()
consensusBuild = nil
// Now test against the real DAG
// submit block1
processBlock(consensusReal, block1, "block1")
// submit chain1
for i, block := range selectedChain {
processBlock(consensusReal, block, fmt.Sprintf("selectedChain block No %d", i))
}
// submit chain2
for i, block := range blocksChain2 {
processBlock(consensusReal, block, fmt.Sprintf("blocksChain2 block No %d", i))
}
// submit a block pointing at tip(chain1) and on first block in chain2 directly
mergeDepthViolatingBlockBottom, isViolatingMergeDepth := checkViolatingMergeDepth(consensusReal, []*externalapi.DomainHash{consensusserialization.BlockHash(blocksChain2[0]), consensusserialization.BlockHash(selectedChain[len(selectedChain)-1])})
if !isViolatingMergeDepth {
t.Fatalf("TestBoundedMergeDepth: Expected mergeDepthViolatingBlockBottom to violate merge depth")
}
// submit a block pointing at tip(chain1) and tip(chain2) should also obviously violate merge depth (this points at first block in chain2 indirectly)
mergeDepthViolatingTop, isViolatingMergeDepth := checkViolatingMergeDepth(consensusReal, []*externalapi.DomainHash{consensusserialization.BlockHash(blocksChain2[len(blocksChain2)-1]), consensusserialization.BlockHash(selectedChain[len(selectedChain)-1])})
if !isViolatingMergeDepth {
t.Fatalf("TestBoundedMergeDepth: Expected mergeDepthViolatingTop to violate merge depth")
}
// the location of the parents in the slices need to be both `-X` so the `selectedChain` one will have higher blueScore (it's a chain longer by 1)
kosherizingBlock, isViolatingMergeDepth := checkViolatingMergeDepth(consensusReal, []*externalapi.DomainHash{consensusserialization.BlockHash(blocksChain2[len(blocksChain2)-3]), consensusserialization.BlockHash(selectedChain[len(selectedChain)-3])})
kosherizingBlockHash := consensusserialization.BlockHash(kosherizingBlock)
if isViolatingMergeDepth {
t.Fatalf("TestBoundedMergeDepth: Expected blueKosherizingBlock to not violate merge depth")
}
virtualGhotDagData, err := consensusReal.GHOSTDAGDataStore().Get(consensusReal.DatabaseContext(), model.VirtualBlockHash)
if err != nil {
t.Fatalf("TestBoundedMergeDepth: Failed getting the ghostdag data of the virtual: %v", err)
}
// Make sure it's actually blue
found := false
for _, blue := range virtualGhotDagData.MergeSetBlues {
if *blue == *kosherizingBlockHash {
found = true
break
}
}
if !found {
t.Fatalf("TestBoundedMergeDepth: Expected kosherizingBlock to be blue by the virtual")
}
pointAtBlueKosherizing, isViolatingMergeDepth := checkViolatingMergeDepth(consensusReal, []*externalapi.DomainHash{kosherizingBlockHash, consensusserialization.BlockHash(selectedChain[len(selectedChain)-1])})
if isViolatingMergeDepth {
t.Fatalf("TestBoundedMergeDepth: Expected selectedTip to not violate merge depth")
}
virtualSelectedParent, err := consensusReal.GetVirtualSelectedParent()
if err != nil {
t.Fatalf("TestBoundedMergeDepth: Failed getting the virtual selected parent %v", err)
}
if *consensusserialization.BlockHash(virtualSelectedParent) != *consensusserialization.BlockHash(pointAtBlueKosherizing) {
t.Fatalf("TestBoundedMergeDepth: Expected %s to be the selectedTip but found %s instead", consensusserialization.BlockHash(pointAtBlueKosherizing), consensusserialization.BlockHash(virtualSelectedParent))
}
// Now let's make the kosherizing block red and try to merge again
tip := consensusserialization.BlockHash(selectedChain[len(selectedChain)-1])
// we use k-1 because `kosherizingBlock` points at tip-2, so 2+k-1 = k+1 anticone.
for i := 0; i < int(params.K)-1; i++ {
block := buildAndInsertBlock(consensusReal, []*externalapi.DomainHash{tip})
tip = consensusserialization.BlockHash(block)
}
virtualSelectedParent, err = consensusReal.GetVirtualSelectedParent()
if err != nil {
t.Fatalf("TestBoundedMergeDepth: Failed getting the virtual selected parent %v", err)
}
if *consensusserialization.BlockHash(virtualSelectedParent) != *tip {
t.Fatalf("TestBoundedMergeDepth: Expected %s to be the selectedTip but found %s instead", tip, consensusserialization.BlockHash(virtualSelectedParent))
}
virtualGhotDagData, err = consensusReal.GHOSTDAGDataStore().Get(consensusReal.DatabaseContext(), model.VirtualBlockHash)
if err != nil {
t.Fatalf("TestBoundedMergeDepth: Failed getting the ghostdag data of the virtual: %v", err)
}
// Make sure it's actually blue
found = false
for _, blue := range virtualGhotDagData.MergeSetBlues {
if *blue == *kosherizingBlockHash {
found = true
break
}
}
if found {
t.Fatalf("expected kosherizingBlock to be red by the virtual")
}
pointAtRedKosherizing, isViolatingMergeDepth := checkViolatingMergeDepth(consensusReal, []*externalapi.DomainHash{kosherizingBlockHash, tip})
if !isViolatingMergeDepth {
t.Fatalf("TestBoundedMergeDepth: Expected selectedTipRedKosherize to violate merge depth")
}
// Now `pointAtBlueKosherizing` itself is actually still blue, so we can still point at that even though we can't point at kosherizing directly anymore
transitiveBlueKosherizing, isViolatingMergeDepth := checkViolatingMergeDepth(consensusReal, []*externalapi.DomainHash{consensusserialization.BlockHash(pointAtBlueKosherizing), tip})
if isViolatingMergeDepth {
t.Fatalf("TestBoundedMergeDepth: Expected transitiveBlueKosherizing to not violate merge depth")
}
virtualSelectedParent, err = consensusReal.GetVirtualSelectedParent()
if err != nil {
t.Fatalf("TestBoundedMergeDepth: Failed getting the virtual selected parent %v", err)
}
if *consensusserialization.BlockHash(virtualSelectedParent) != *consensusserialization.BlockHash(transitiveBlueKosherizing) {
t.Fatalf("TestBoundedMergeDepth: Expected %s to be the selectedTip but found %s instead", consensusserialization.BlockHash(transitiveBlueKosherizing), consensusserialization.BlockHash(virtualSelectedParent))
}
// Lets validate the status of all the interesting blocks
if getStatus(consensusReal, pointAtBlueKosherizing) != externalapi.StatusValid {
t.Fatalf("TestBoundedMergeDepth: pointAtBlueKosherizing expected status '%s' but got '%s'", externalapi.StatusValid, getStatus(consensusReal, pointAtBlueKosherizing))
}
if getStatus(consensusReal, pointAtRedKosherizing) != externalapi.StatusInvalid {
t.Fatalf("TestBoundedMergeDepth: pointAtRedKosherizing expected status '%s' but got '%s'", externalapi.StatusInvalid, getStatus(consensusReal, pointAtRedKosherizing))
}
if getStatus(consensusReal, transitiveBlueKosherizing) != externalapi.StatusValid {
t.Fatalf("TestBoundedMergeDepth: transitiveBlueKosherizing expected status '%s' but got '%s'", externalapi.StatusValid, getStatus(consensusReal, transitiveBlueKosherizing))
}
if getStatus(consensusReal, mergeDepthViolatingBlockBottom) != externalapi.StatusInvalid {
t.Fatalf("TestBoundedMergeDepth: mergeDepthViolatingBlockBottom expected status '%s' but got '%s'", externalapi.StatusInvalid, getStatus(consensusReal, mergeDepthViolatingBlockBottom))
}
if getStatus(consensusReal, mergeDepthViolatingTop) != externalapi.StatusInvalid {
t.Fatalf("TestBoundedMergeDepth: mergeDepthViolatingTop expected status '%s' but got '%s'", externalapi.StatusInvalid, getStatus(consensusReal, mergeDepthViolatingTop))
}
if getStatus(consensusReal, kosherizingBlock) != externalapi.StatusUTXOPendingVerification {
t.Fatalf("kosherizingBlock expected status '%s' but got '%s'", externalapi.StatusUTXOPendingVerification, getStatus(consensusReal, kosherizingBlock))
}
for i, b := range blocksChain2 {
if getStatus(consensusReal, b) != externalapi.StatusUTXOPendingVerification {
t.Fatalf("blocksChain2[%d] expected status '%s' but got '%s'", i, externalapi.StatusUTXOPendingVerification, getStatus(consensusReal, b))
}
}
for i, b := range selectedChain {
if getStatus(consensusReal, b) != externalapi.StatusValid {
t.Fatalf("selectedChain[%d] expected status '%s' but got '%s'", i, externalapi.StatusValid, getStatus(consensusReal, b))
}
}
})
}

View File

@@ -6,12 +6,39 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
// It's ordered in the same way as the block merge set blues.
type AcceptanceData []*BlockAcceptanceData
// Clone clones the AcceptanceData
func (ad AcceptanceData) Clone() AcceptanceData {
if ad == nil {
return nil
}
clone := make(AcceptanceData, len(ad))
for i, blockAcceptanceData := range ad {
clone[i] = blockAcceptanceData.Clone()
}
return clone
}
// BlockAcceptanceData stores all transactions in a block with an indication
// if they were accepted or not by some other block
type BlockAcceptanceData struct {
TransactionAcceptanceData []*TransactionAcceptanceData
}
// Clone returns a clone of BlockAcceptanceData
func (bad *BlockAcceptanceData) Clone() *BlockAcceptanceData {
if bad == nil {
return nil
}
clone := &BlockAcceptanceData{TransactionAcceptanceData: make([]*TransactionAcceptanceData, len(bad.TransactionAcceptanceData))}
for i, acceptanceData := range bad.TransactionAcceptanceData {
clone.TransactionAcceptanceData[i] = acceptanceData.Clone()
}
return clone
}
// TransactionAcceptanceData stores a transaction together with an indication
// if it was accepted or not by some block
type TransactionAcceptanceData struct {
@@ -19,3 +46,16 @@ type TransactionAcceptanceData struct {
Fee uint64
IsAccepted bool
}
// Clone returns a clone of TransactionAcceptanceData
func (tad *TransactionAcceptanceData) Clone() *TransactionAcceptanceData {
if tad == nil {
return nil
}
return &TransactionAcceptanceData{
Transaction: tad.Transaction.Clone(),
Fee: tad.Fee,
IsAccepted: tad.IsAccepted,
}
}

View File

@@ -7,3 +7,15 @@ type BlockRelations struct {
Parents []*externalapi.DomainHash
Children []*externalapi.DomainHash
}
// Clone returns a clone of BlockRelations
func (br *BlockRelations) Clone() *BlockRelations {
if br == nil {
return nil
}
return &BlockRelations{
Parents: externalapi.CloneHashes(br.Parents),
Children: externalapi.CloneHashes(br.Children),
}
}

View File

@@ -6,6 +6,23 @@ type DomainBlock struct {
Transactions []*DomainTransaction
}
// Clone returns a clone of DomainBlock
func (block *DomainBlock) Clone() *DomainBlock {
if block == nil {
return nil
}
transactionClone := make([]*DomainTransaction, len(block.Transactions))
for i, tx := range block.Transactions {
transactionClone[i] = tx.Clone()
}
return &DomainBlock{
Header: block.Header.Clone(),
Transactions: transactionClone,
}
}
// DomainBlockHeader represents the header part of a Kaspa block
type DomainBlockHeader struct {
Version int32
@@ -17,3 +34,21 @@ type DomainBlockHeader struct {
Bits uint32
Nonce uint64
}
// Clone returns a clone of DomainBlockHeader
func (header *DomainBlockHeader) Clone() *DomainBlockHeader {
if header == nil {
return nil
}
return &DomainBlockHeader{
Version: header.Version,
ParentHashes: CloneHashes(header.ParentHashes),
HashMerkleRoot: *header.HashMerkleRoot.Clone(),
AcceptedIDMerkleRoot: *header.AcceptedIDMerkleRoot.Clone(),
UTXOCommitment: *header.UTXOCommitment.Clone(),
TimeInMilliseconds: header.TimeInMilliseconds,
Bits: header.Bits,
Nonce: header.Nonce,
}
}

View File

@@ -3,6 +3,11 @@ package externalapi
// BlockStatus represents the validation state of the block.
type BlockStatus byte
// Clone returns a clone of BlockStatus
func (bs BlockStatus) Clone() BlockStatus {
return bs
}
const (
// StatusInvalid indicates that the block is invalid.
StatusInvalid BlockStatus = iota
@@ -21,3 +26,15 @@ const (
// StatusHeaderOnly indicates that the block transactions are not held (pruned or wasn't added yet)
StatusHeaderOnly
)
var blockStatusStrings = map[BlockStatus]string{
StatusInvalid: "Invalid",
StatusValid: "Valid",
StatusUTXOPendingVerification: "UTXOPendingVerification",
StatusDisqualifiedFromChain: "DisqualifiedFromChain",
StatusHeaderOnly: "HeaderOnly",
}
func (bs BlockStatus) String() string {
return blockStatusStrings[bs]
}

View File

@@ -0,0 +1,21 @@
package externalapi
// Consensus maintains the current core state of the node
type Consensus interface {
BuildBlock(coinbaseData *DomainCoinbaseData, transactions []*DomainTransaction) (*DomainBlock, error)
ValidateAndInsertBlock(block *DomainBlock) error
ValidateTransactionAndPopulateWithConsensusData(transaction *DomainTransaction) error
GetBlock(blockHash *DomainHash) (*DomainBlock, error)
GetBlockHeader(blockHash *DomainHash) (*DomainBlockHeader, error)
GetBlockInfo(blockHash *DomainHash) (*BlockInfo, error)
GetHashesBetween(lowHash, highHash *DomainHash) ([]*DomainHash, error)
GetMissingBlockBodyHashes(highHash *DomainHash) ([]*DomainHash, error)
GetPruningPointUTXOSet(expectedPruningPointHash *DomainHash) ([]byte, error)
SetPruningPointUTXOSet(serializedUTXOSet []byte) error
GetVirtualSelectedParent() (*DomainBlock, error)
CreateBlockLocator(lowHash, highHash *DomainHash) (BlockLocator, error)
FindNextBlockLocatorBoundaries(blockLocator BlockLocator) (lowHash, highHash *DomainHash, err error)
GetSyncInfo() (*SyncInfo, error)
}

View File

@@ -17,6 +17,25 @@ func (hash DomainHash) String() string {
return hex.EncodeToString(hash[:])
}
// Clone clones the hash
func (hash *DomainHash) Clone() *DomainHash {
if hash == nil {
return nil
}
hashClone := *hash
return &hashClone
}
// CloneHashes returns a clone of the given hashes slice
func CloneHashes(hashes []*DomainHash) []*DomainHash {
clone := make([]*DomainHash, len(hashes))
for i, hash := range hashes {
clone[i] = hash.Clone()
}
return clone
}
// DomainHashesToStrings returns a slice of strings representing the hashes in the given slice of hashes
func DomainHashesToStrings(hashes []*DomainHash) []string {
strings := make([]string, len(hashes))

View File

@@ -15,3 +15,13 @@ func (id DomainSubnetworkID) String() string {
}
return hex.EncodeToString(id[:])
}
// Clone returns a clone of DomainSubnetworkID
func (id *DomainSubnetworkID) Clone() *DomainSubnetworkID {
if id == nil {
return nil
}
idClone := *id
return &idClone
}

View File

@@ -1,9 +1,11 @@
package externalapi
import "fmt"
// Each of the following represent one of the possible sync
// states of the consensus
const (
SyncStateNormal SyncState = iota
SyncStateRelay SyncState = iota
SyncStateMissingGenesis
SyncStateHeadersFirst
SyncStateMissingUTXOSet
@@ -15,8 +17,10 @@ type SyncState uint8
func (s SyncState) String() string {
switch s {
case SyncStateNormal:
return "SyncStateNormal"
case SyncStateRelay:
return "SyncStateRelay"
case SyncStateMissingGenesis:
return "SyncStateMissingGenesis"
case SyncStateHeadersFirst:
return "SyncStateHeadersFirst"
case SyncStateMissingUTXOSet:
@@ -25,11 +29,13 @@ func (s SyncState) String() string {
return "SyncStateMissingBlockBodies"
}
return "<unknown state>"
return fmt.Sprintf("<unknown state (%d)>", s)
}
// SyncInfo holds info about the current sync state of the consensus
type SyncInfo struct {
State SyncState
IBDRootUTXOBlockHash *DomainHash
HeaderCount uint64
BlockCount uint64
}

View File

@@ -17,6 +17,43 @@ type DomainTransaction struct {
Fee uint64
Mass uint64
// ID is a field that is used to cache the transaction ID.
// Always use consensusserialization.TransactionID instead of accessing this field directly
ID *DomainTransactionID
}
// Clone returns a clone of DomainTransaction
func (tx *DomainTransaction) Clone() *DomainTransaction {
if tx == nil {
return nil
}
payloadClone := make([]byte, len(tx.Payload))
copy(payloadClone, tx.Payload)
inputsClone := make([]*DomainTransactionInput, len(tx.Inputs))
for i, input := range tx.Inputs {
inputsClone[i] = input.Clone()
}
outputsClone := make([]*DomainTransactionOutput, len(tx.Outputs))
for i, output := range tx.Outputs {
outputsClone[i] = output.Clone()
}
return &DomainTransaction{
Version: tx.Version,
Inputs: inputsClone,
Outputs: outputsClone,
LockTime: tx.LockTime,
SubnetworkID: *tx.SubnetworkID.Clone(),
Gas: tx.Gas,
PayloadHash: *tx.PayloadHash.Clone(),
Payload: payloadClone,
Fee: tx.Fee,
Mass: tx.Mass,
}
}
// DomainTransactionInput represents a Kaspa transaction input
@@ -28,12 +65,41 @@ type DomainTransactionInput struct {
UTXOEntry *UTXOEntry
}
// Clone returns a clone of DomainTransactionInput
func (input *DomainTransactionInput) Clone() *DomainTransactionInput {
if input == nil {
return nil
}
signatureScriptClone := make([]byte, len(input.SignatureScript))
copy(signatureScriptClone, input.SignatureScript)
return &DomainTransactionInput{
PreviousOutpoint: *input.PreviousOutpoint.Clone(),
SignatureScript: signatureScriptClone,
Sequence: input.Sequence,
UTXOEntry: input.UTXOEntry.Clone(),
}
}
// DomainOutpoint represents a Kaspa transaction outpoint
type DomainOutpoint struct {
TransactionID DomainTransactionID
Index uint32
}
// Clone returns a clone of DomainOutpoint
func (op *DomainOutpoint) Clone() *DomainOutpoint {
if op == nil {
return nil
}
return &DomainOutpoint{
TransactionID: *op.TransactionID.Clone(),
Index: op.Index,
}
}
// String stringifies an outpoint.
func (op DomainOutpoint) String() string {
return fmt.Sprintf("(%s: %d)", op.TransactionID, op.Index)
@@ -53,6 +119,21 @@ type DomainTransactionOutput struct {
ScriptPublicKey []byte
}
// Clone returns a clone of DomainTransactionOutput
func (output *DomainTransactionOutput) Clone() *DomainTransactionOutput {
if output == nil {
return nil
}
scriptPublicKeyClone := make([]byte, len(output.ScriptPublicKey))
copy(scriptPublicKeyClone, output.ScriptPublicKey)
return &DomainTransactionOutput{
Value: output.Value,
ScriptPublicKey: scriptPublicKeyClone,
}
}
// DomainTransactionID represents the ID of a Kaspa transaction
type DomainTransactionID DomainHash
@@ -60,3 +141,13 @@ type DomainTransactionID DomainHash
func (id DomainTransactionID) String() string {
return DomainHash(id).String()
}
// Clone returns a clone of DomainTransactionID
func (id *DomainTransactionID) Clone() *DomainTransactionID {
if id == nil {
return nil
}
idClone := *id
return &idClone
}

View File

@@ -11,6 +11,23 @@ type UTXOEntry struct {
IsCoinbase bool
}
// Clone returns a clone of UTXOEntry
func (entry *UTXOEntry) Clone() *UTXOEntry {
if entry == nil {
return nil
}
scriptPublicKeyClone := make([]byte, len(entry.ScriptPublicKey))
copy(scriptPublicKeyClone, entry.ScriptPublicKey)
return &UTXOEntry{
Amount: entry.Amount,
ScriptPublicKey: scriptPublicKeyClone,
BlockBlueScore: entry.BlockBlueScore,
IsCoinbase: entry.IsCoinbase,
}
}
// NewUTXOEntry creates a new utxoEntry representing the given txOut
func NewUTXOEntry(amount uint64, scriptPubKey []byte, isCoinbase bool, blockBlueScore uint64) *UTXOEntry {
return &UTXOEntry{

View File

@@ -11,5 +11,25 @@ type BlockGHOSTDAGData struct {
BluesAnticoneSizes map[externalapi.DomainHash]KType
}
// Clone returns a clone of BlockGHOSTDAGData
func (bgd *BlockGHOSTDAGData) Clone() *BlockGHOSTDAGData {
if bgd == nil {
return nil
}
bluesAnticoneSizesClone := make(map[externalapi.DomainHash]KType, len(bgd.BluesAnticoneSizes))
for hash, size := range bgd.BluesAnticoneSizes {
bluesAnticoneSizesClone[hash] = size
}
return &BlockGHOSTDAGData{
BlueScore: bgd.BlueScore,
SelectedParent: bgd.SelectedParent.Clone(),
MergeSetBlues: externalapi.CloneHashes(bgd.MergeSetBlues),
MergeSetReds: externalapi.CloneHashes(bgd.MergeSetReds),
BluesAnticoneSizes: bluesAnticoneSizesClone,
}
}
// KType defines the size of GHOSTDAG consensus algorithm K parameter.
type KType byte

View File

@@ -11,4 +11,5 @@ type BlockHeaderStore interface {
HasBlockHeader(dbContext DBReader, blockHash *externalapi.DomainHash) (bool, error)
BlockHeaders(dbContext DBReader, blockHashes []*externalapi.DomainHash) ([]*externalapi.DomainBlockHeader, error)
Delete(blockHash *externalapi.DomainHash)
Count() uint64
}

View File

@@ -11,4 +11,5 @@ type BlockStore interface {
HasBlock(dbContext DBReader, blockHash *externalapi.DomainHash) (bool, error)
Blocks(dbContext DBReader, blockHashes []*externalapi.DomainHash) ([]*externalapi.DomainBlock, error)
Delete(blockHash *externalapi.DomainHash)
Count() uint64
}

View File

@@ -0,0 +1,18 @@
package model
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
// BlockBuilder is responsible for creating blocks from the current state
type BlockBuilder interface {
BuildBlock(coinbaseData *externalapi.DomainCoinbaseData, transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlock, error)
}
// TestBlockBuilder adds to the main BlockBuilder methods required by tests
type TestBlockBuilder interface {
BlockBuilder
// BuildBlockWithParents builds a block with provided parents, coinbaseData and transactions,
// and returns the block together with its past UTXO-diff from the virtual.
BuildBlockWithParents(parentHashes []*externalapi.DomainHash, coinbaseData *externalapi.DomainCoinbaseData,
transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlock, *UTXODiff, error)
}

Some files were not shown because too many files have changed in this diff Show More