Compare commits

..

175 Commits

Author SHA1 Message Date
Elichai Turkel
7de07cdc97 Add a github action deploy script to build and publish releases 2021-03-08 18:17:35 +02:00
Svarog
921ca19b42 Update testnet genesis and testnet network name (#1582)
* Update testnet gensis and testnet network name

* Move genesis closer to the present
2021-03-08 15:04:18 +02:00
Mike Zak
98c2dc8189 Update to version 0.9.1 2021-03-08 11:49:38 +02:00
Mike Zak
37654156a6 Update changelog for v0.9.0 2021-03-04 10:53:00 +02:00
Ori Newman
0271858f25 Readd BlockHashes to getBlocks response (#1575) 2021-03-03 16:27:51 +02:00
Elichai Turkel
7909480757 Merge big subdags in pick virtual parents (#1574)
* Refactor mergeSetIncrease to return the current BFS block to allow easier merging

* Remove unneeded Heap/HashSet usages

* Add new IsAnyAncestorOf to DagTopolyManager

* Check if the new candidate is in the future of any existing candidate

* Add comments and fix off-by-one in the mergeSetIncrease queue

* Fixed DAGToplogy test mock

* Fix review comments
2021-03-03 16:17:16 +02:00
Ori Newman
18274c236b Write in the reject message the tx rejection reason (#1573)
Co-authored-by: Elichai Turkel <elichai.turkel@gmail.com>
2021-03-02 20:11:31 +02:00
Elichai Turkel
7829a9fd76 Add nil checks for protowire (#1570)
* Handle errors in p2p handshake better

* Add a new errNil in protowire

* Add nil checks for all protowire.toAppMessage() functions for the p2p

* Add nil checks for all protowire.toAppMessage() functions for the RPC

* Add nil check for protwire KaspadMessage

Co-authored-by: Svarog <feanorr@gmail.com>
2021-03-02 19:14:31 +02:00
Ori Newman
1548ed9629 Increase getBlocks limit to 1000 (#1572) 2021-03-02 18:24:37 +02:00
Svarog
32cd643e8b Clone transactions before returning them out of mempool (#1571)
Co-authored-by: Elichai Turkel <elichai.turkel@gmail.com>
2021-03-02 17:28:53 +02:00
Ori Newman
05df6e3e4e Return RPC error if getBlock's lowHash doesn't exist (#1569)
Co-authored-by: Svarog <feanorr@gmail.com>
2021-03-02 16:21:55 +02:00
Svarog
ce326b3c7d Add default dns-seeder to testnet (#1568) 2021-03-02 12:57:16 +02:00
Svarog
a4ae263ed5 Don't print stack trace on disconnected (#1567) 2021-03-02 12:25:33 +02:00
Elichai Turkel
79be1edaa5 Fix utxoindex deserialization (#1566)
* Fix broken deserialization in utxoindex

* Add Tests for hashes serialization in utxo index
2021-03-01 19:06:19 +02:00
Svarog
c1ef5f0c56 Add pruning point hash to GetBlockDagInfo response (#1565)
Co-authored-by: Ori Newman <orinewman1@gmail.com>
2021-03-01 18:46:45 +02:00
Elichai Turkel
458e409654 Rename handleRequestBlocksFlow to handleRequestHeadersFlow (#1563) 2021-03-01 15:01:59 +02:00
Svarog
df19bdfaf3 Use EmitUnpopulated so that kaspactl prints all fields, even the default ones (#1561)
Co-authored-by: Elichai Turkel <elichai.turkel@gmail.com>
2021-03-01 14:47:34 +02:00
Elichai Turkel
103edf97d0 Stop logging an error whenever an RPC/P2P connection is canceled (#1562)
* Don't log error when connectionLoop is canceled

* Print new line after Exiting...

* Add stacktrace to the unknown error from connectionLoops
2021-03-01 14:37:42 +02:00
Elichai Turkel
1f69f9eed9 Cleanup the logger and make it asynchronous (#1524)
* Remove Subsystems map and replace with RegisterSubSystem

* Clean up the logger

* Fix LOGFLAGS and make LongFile work correctly

* Parallelize the logger backend

* More logger cleanup

* Initialize and close the logger backend wherever it's needed

* Move the location where the backend is closed, also print the log if it panics while writing

* Add TestMain to reachability manager tests to preserve the same log level

* Fix review comments

Co-authored-by: Svarog <feanorr@gmail.com>
2021-03-01 14:04:40 +02:00
Elichai Turkel
6a12428504 Close iterators (#1542)
* Add Close() function to all the iterators

* Add defer iterator.Close() whenever we open an iterator

* Add isClosed to all iterators and panic/return error if used after closing

Co-authored-by: Svarog <feanorr@gmail.com>
2021-03-01 11:15:59 +02:00
Svarog
63b1d2a05a Add childrenHashes to GetBlock/s RPC commands (#1560)
* Add childrenHashes to GetBlock/s RPC commands

* Fix missed error + implement GetBlockChildren in fakeRelayInvsContext
2021-02-28 18:28:29 +02:00
Svarog
089115c389 Add ScriptPublicKey.Version to RPC (#1559)
Co-authored-by: Ori Newman <orinewman1@gmail.com>
2021-02-25 17:43:06 +02:00
Elichai Turkel
24a12cf2a1 Fix subnetworks FromString and disable reversal (#1557)
* Remove DomainSubnetworkID reversal

* Fix DomaiNSubnetworkID FromString implementation

* Change RPC conversation logic to use Stringer/FromString

Co-authored-by: Ori Newman <orinewman1@gmail.com>
2021-02-25 16:35:31 +02:00
Mike Zak
6597f24bab Add changelog for v0.8.10 2021-02-25 14:56:05 +02:00
Svarog
dc84913214 Convert ProtocolError to value-type, so that it can be used withh errors.As + fix SubmitBlock ProtocolError condition (#1555)
* Fix condition from || to &&

* Convert ProtocolError to value-type, so that it can be used wihth errors.As

* Simplify condition further
2021-02-24 17:13:59 +02:00
Ori Newman
201646282b Fix the target block rate to create less bursty mining (#1554)
Co-authored-by: Svarog <feanorr@gmail.com>
2021-02-24 16:35:38 +02:00
Svarog
3f08bf87a8 Fix mempool.RemoveTransactions (#1551)
* Fix mempool.RemoveTransactions

* Don't crash if mempool.RemoveTransactions returns an error. log.Critical instead
2021-02-24 12:47:21 +02:00
Ori Newman
581a12db96 Add RPC reconnection to the miner (#1552)
* Add RPC reconnection to the miner

* Fix wrapf

* Change logs
2021-02-24 10:25:13 +02:00
Svarog
fb6c9c8f21 Remove virtual diff parents (#1550)
* resolveSingleBlockStatus: If the block being resolved is not going to be the next selectedTip - set it's diffParent to be the old selectedTip

* resolveSingleBlockStatus: If the block being resolved is going to be the next selectedTip - set it as old selectedTip's diffChild

* Remove any mentions of virtualDiffParents

* If block is genesis - don't do all the mumbo-jumbo with oldSelectedTip

* Check an unchecked error

* Write a better log message
2021-02-23 17:19:35 +02:00
Ori Newman
2adb4f5d0f Fix UTXO index (#1548)
* Add VirtualUTXODiff and VirtualParents to block insertion result

* Add GetVirtualUTXOs

* Add OnPruningPointUTXOSetOverrideHandler

* Add recovery to UTXO index

* Add UTXO set override notification

* Fix compilation error

* Fix iterators in UTXO index and fix TestUTXOIndex

* Change Dialing to DEBUG

* Change LogBlock location

* Rename StopNotify to StopNotifying

* Add sanity check

* Add comment

* Remove receiver from serialization functions

Co-authored-by: Elichai Turkel <elichai.turkel@gmail.com>
2021-02-23 16:51:51 +02:00
Ori Newman
9ffda2b1da Change disconnect log level to INFO (#1549)
* Change disconnect log level to INFO

* Add disconnected log and change log levels
2021-02-23 13:52:47 +02:00
Svarog
bee0893660 Renamed BlueBlockWindow to just BlockWindow (#1547)
* Renamed BlueBlockWindow to just BlockWindow

* Update comment
2021-02-22 11:33:39 +02:00
talelbaz
a7bb1853f9 Adds tests for transaction validator and block validators (#1531)
* [NOD-1453] cover failing block validation

* [NOD-1453] Complete covering test for invalid block

* [NOD-1453] Fix validator tests after rebase

* [NOD-1453] Cover tests for valid blocks

* [NOD-1453] Implement unit tests for ValidateTransactionInIsolation

* [NOD-1453] Add tests for ValidateTransactionInContextAndPopulateMassAndFee

* [NOD-1453] Cover ValidateHeaderInContext test

* [NOD-1453] Fix after rebase

* not finish

* commited for update the branch.

* Adds new tests to block_body_in_isolation_test.go according to (and instead of ) blockvalisator_test.go

* Adds a comment to type MEDIAN.

* Fixes according to the review notes: add notes and change variables name.

* Fix comment.

* Remove an unused test( all the tests in this file were passed to other test files).

* Change a variable name(txWithAnEmptyInvalidScript to txWithInvalidSignature).

* adds missing '}'.

* Change spaces to tab

Co-authored-by: karim1king <karimkaspersky@yahoo.com>
Co-authored-by: Karim A <karim.a@it-dimension.com>
Co-authored-by: tal <tal@daglabs.com>
2021-02-21 17:46:22 +02:00
talelbaz
35e555e959 Tests validateDifficulty ( ValidatePruningPointViolationAndProofOfWorkAndDifficulty) (#1532)
* Adds tests for validateDifficulty

* fixes according to the review notes: adding the test's goal and fix an unmatch test name on the NewTestConsensus.

* Fixes according to the review notes:delete the function genesisBits - No usages.

* Fix according to review - fix comments.

Co-authored-by: tal <tal@daglabs.com>
Co-authored-by: Ori Newman <orinewman1@gmail.com>
2021-02-21 17:00:34 +02:00
Elichai Turkel
a250f697ee Prevent fast failing (#1545) 2021-02-21 16:09:19 +02:00
Elichai Turkel
f66708b3c6 Make the CI more verbose and cache kaspad dependencies in the dockerfile (#1541)
* Make the CI more verbose

* Improve the docker caching of kaspad dependencies

Co-authored-by: Svarog <feanorr@gmail.com>
2021-02-21 12:15:32 +02:00
Svarog
8fbea5d239 Increase the sleep time in kaspaminer when the node is not synced (#1544)
Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com>
2021-02-21 11:46:06 +02:00
Svarog
5fa06fe7d7 Upgrade everything to go1.16 (#1539)
Co-authored-by: Ori Newman <orinewman1@gmail.com>
2021-02-21 09:17:21 +02:00
Ori Newman
06fd6f1b95 Parallelize tests on Dockerfile (#1540)
Co-authored-by: Svarog <feanorr@gmail.com>
2021-02-18 11:38:44 +02:00
Ori Newman
d2f4ed660c Disallow header only blocks on RPC, relay and when requesting IBD full blocks (#1537) 2021-02-18 10:39:12 +02:00
Elichai Turkel
19878aa062 Make templateManager hold a DomainBlock and isSynced bool instead of a GetBlockTemplateResponseMessage (#1538) 2021-02-18 00:59:11 +02:00
Elichai Turkel
6415e525c3 go test race detector in github actions at cron job (#1534)
Co-authored-by: Ori Newman <orinewman1@gmail.com>
2021-02-17 18:59:42 +02:00
Svarog
995e526dae Make antiPastHashesBetween return blocks sorted in ghostdag-order (#1536)
* Make antiPastHashesBetween return blocks sorted in ghostdag-order

* Return sortedMergeSet instead of blueMergeSet

* Invert the order of parameters of IsAncestorOf

* Add RenderDAGToDot to TestConsensus

* Add HighHash explicitly, unless lowHash == highHash

* Use Equal instead of == when comparing hashes

* Fixed TestSyncManager_GetHashesBetween

* Fix tests

* findHighHashAccordingToMaxBlueScoreDifference: don't start looking if the whole thing fits

* Handle a missed error

* Remove redundant call to RenderToDot

* Fix bug in findHighHashAccordingToMaxBlueScoreDifference
2021-02-17 18:22:08 +02:00
Elichai Turkel
00a023620d Fix a data race in the block logger (#1533) 2021-02-17 17:05:25 +02:00
Ori Newman
2908a46441 Don't ban when sending pruned blocks (#1530) 2021-02-15 16:43:35 +02:00
Ori Newman
e78cdff3d0 Don't mark block that got rejected because of ruleerrors.ErrPrunedBlock as invalid (#1529)
* Don't mark block that got rejected because of ruleerrors.ErrPrunedBlock as invalid

* Update comment
2021-02-15 15:34:21 +02:00
Ori Newman
2a31074fc4 Make getBlock return an error for invalid blocks (#1528) 2021-02-15 14:39:25 +02:00
stasatdaglabs
d835f72e74 Make AddressManager persistent (#1525)
* Move existing address/bannedAddress functionality to a new addressStore object.

* Implement TestAddressManager.

* Implement serializeAddressKey and deserializeAddressKey.

* Implement serializeNetAddress and deserializeNetAddress.

* Store addresses and banned addresses to disk.

* Implement restoreNotBannedAddresses.

* Fix bannedDatabaseKey.

* Implement restoreBannedAddresses.

* Implement TestRestoreAddressManager.

* Defer closing the database in TestRestoreAddressManager.

* Defer closing the database in TestRestoreAddressManager.

* Add a log.

* Return errors where appropriate.

Co-authored-by: Elichai Turkel <elichai.turkel@gmail.com>
2021-02-14 19:08:06 +02:00
Elichai Turkel
a581dea127 Remove unused utils and structures (#1526)
* Remove unused utils

* Remove unneeded randomness from tests

* Remove more unused functions

* Remove unused protobuf structures

* Fix small errors
2021-02-14 18:13:20 +02:00
stasatdaglabs
7b4b5668e2 Enhance UTXOsChanged notifications (#1522)
* In PropagateUTXOsChangedNotifications, add the given addresses to the address list instead of replacing them.

* Add StopNotifyingUtxosChangedRequestMessage to rpc.proto.

* Implement StopNotifyingUTXOsChanged.

* Optimize convertUTXOChangesToUTXOsChangedNotification.
2021-02-14 12:58:29 +02:00
Elichai Turkel
0e2061d838 Make RPC command GetBlocks prepend lowHash to return value and fix error when lowHash=highHash (#1520)
* Don't error out if antiPastHashesBetween have 2 blocks with the same blue score

* Prepend lowHash to RPC GetBlocks request

* Add a test for GetHashesBetween

* Add a test for GetBlocks RPC call

* Update antipast.go

Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com>
2021-02-11 18:13:46 +02:00
Svarog
0a579e7f78 DownloadHeaders: Instead of using doneChan - close blockHeadersMessageChan. (#1523) 2021-02-11 17:01:15 +02:00
hashdag
1ed6c4c086 Update README.md 2021-02-11 15:04:15 +02:00
Svarog
fea83e5c6c Change Testnet name to kaspad-testnet-2 (#1521)
* Change Testnet name to kaspad-testnet-2

* Fix tests that hardcoded network names
2021-02-11 15:02:25 +02:00
Elichai Turkel
7c3beb526e Limit stdout log level to info (#1518)
* Rename debuglevel to loglevel

* Limit stdout level to info by default

Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com>
Co-authored-by: Svarog <feanorr@gmail.com>
2021-02-10 18:35:13 +02:00
Svarog
171deded4e Implement GetBlocks RPC command (#1514)
* Remove BlockHexes from GetBlocks request and response

* Add GetBlocks RPC

* Include the selectedTip's anticone in GetBlocks

* Add Anticone to fakeRelayInvsContext

* Include verbose data only if it was requested + Add comments to HandleGetBlocks

* Allow antiPastHashesBetween to receive unrelated low and high hashes

* Convert to/from protowire GetBlocksResponse with no verbose data correctly

* Removed NextLowHash

* Update GetBlocks in rpc_client

* Validate in consensus.Anticone that blockHash exists

Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com>
2021-02-10 18:27:04 +02:00
stasatdaglabs
94cdc77481 Send peers the hash of the virtual selected parent once connection is established (#1519)
* Send peers the hash of the virtual selected parent once connection is established.

* Add a log to SendVirtualSelectedParentInv.

* Fix TestIBDWithPruning.

* Fix TestIBDWithPruning better and signal from the IBD syncer to the IBD syncee that the DAG is split amongst them.

* Fix TestVirtualSelectedParentChain.

* Add comments.
2021-02-10 18:09:25 +02:00
Svarog
1222a555f2 Prune blocks below pruning point when moving pruning point during IBD (#1513)
* Split deletePastBlocks into sub-routines

* Remove SelectedParentIterator, and refactor SelectedChildIterator to support First and Error

* Implement PruneAllBlocks

* Prune all blocks in the store

* Prune only blocks that are below the pruning point

* Defer call onEnd of LogAndMeasureExecutionTime

* Handle a forgotten error

* Minor style fixes
2021-02-10 16:39:36 +02:00
talelbaz
f13fc35b9e Adds new tests for "BlockAtDepth" function and validate the old tests on DAGTraversal. (#1500)
* Adds tests for the "blockAtDepth" function and verify old other tests.

* Optimization on create the Dag chain.

* Changes according to the review - more detailed error messages, added constants, changed to 3 independent graphs (instead of extending), and changes all the abbreviations.

* Changes according to the review - divide the test into three separate tests and change names to variables.

* Changes according to the review - the order of the function has changed.

* delete double lines

Co-authored-by: tal <tal@daglabs.com>
Co-authored-by: Svarog <feanorr@gmail.com>
2021-02-09 15:28:37 +02:00
Elichai Turkel
2d61a67592 Change some logs (#1511) 2021-02-09 14:00:02 +02:00
stasatdaglabs
3a4fa6e0e1 Add blockVerboseData to blockAddedNotifications (#1508)
* Add blockVerboseData to blockAddedNotifications.

* Run the documentation generator.
2021-02-09 10:30:16 +02:00
Elichai Turkel
2edf6bfd07 Minimize memory usage in tests (#1495)
* Make leveldb cache configurable

* Fix leveldb tests

* Add a preallocate option to all caches and disable in tests

Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com>
2021-02-08 18:37:02 +02:00
Ori Newman
8225f7fb3c Add GetInfo RPC command (#1504)
* Add GetInfo RPC command

* Rename ID to p2p ID
2021-02-08 16:33:21 +02:00
Elichai Turkel
3d0a2a47b2 Move testGHOSTDagSorter to testutils, and build a boilerplate for overriding specific managers (#1486)
* Move testGHOSTDagSorter to testutils

* Allow overriding managers in consensus, starting with ghostdag

* Add test prefix to SetDataDir and SetGHOSTDAGManager

Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com>
2021-02-08 15:24:26 +02:00
Ori Newman
4a354cd538 Validate transactions on BuildBlock (#1491)
* Validate transactions on BuildBlock

* Rename tx -> transactions

* Add transaction validator to block builder constructor and fix TestValidateAndInsertErrors

Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com>
2021-02-08 14:59:43 +02:00
Ori Newman
1a3b16aaa3 Don't change the new reindex root if the blue score of the selected tip is lower than the current reindex root (#1501) 2021-02-08 14:00:53 +02:00
Ori Newman
5b5a7e60af Add aggregated headers processing logs (#1487)
* Add aggregated headers processing logs

* Unite headers and blocks log

Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com>
2021-02-08 10:45:13 +02:00
Ori Newman
d30f05b250 Remove IsPushOnlyScript from mempool validation (#1492)
* Remove IsPushOnlyScript from mempool validation

* Fix TestCheckTransactionStandard
2021-02-08 10:04:19 +02:00
Svarog
6bc7a4eb85 Allow GetMissingBlockBodyHashes return an empty list if the missing blocks were requested before IBD start (#1498)
* Allow GetMissingBlockBodyHashes return an empty list if the missing blocks were requested before IBD start

* Add link to issue in comment about error to be fixed
2021-02-07 16:12:15 +02:00
Ori Newman
608d1f8ef9 Add TestBlueWork (#1488)
* Add TestBlueWork

* Add comments and blue score check

Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com>
2021-02-04 17:12:33 +02:00
stasatdaglabs
a792d4a19e Don't fsync immediately after all writes (#1490) 2021-02-04 16:36:46 +02:00
stasatdaglabs
8941c518fc Remove the no-longer relevant highHashReceived mechanism in syncHeaders. (#1489) 2021-02-04 16:06:20 +02:00
Ori Newman
6f53da18b1 Increase stores cache (#1485)
Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com>
2021-02-04 10:06:02 +02:00
stasatdaglabs
44280b9006 Require the --miningaddr parameter in kaspaminer. (#1482)
Co-authored-by: Ori Newman <orinewman1@gmail.com>
2021-02-04 09:42:02 +02:00
Ori Newman
dbababb978 Limit mempool size to million transactions and remove the least profitable transactions (#1483)
* Limit mempool size to million transactions and remove the least profitable transactions

* Simplify insert

* Fix typo

* Improve findTxIndexInOrderedTransactionsByFeeRate readability
2021-02-03 19:45:39 +02:00
Ori Newman
238950cb98 Add logs (#1484)
* Add logs

* Fix log name
2021-02-03 17:47:59 +02:00
Ori Newman
ee8fa32ff8 Refactor miner and mine when waiting for block to validate (#1481)
* Refactor miner and mine when waiting for block to validate

* Fix -n to work after the refactor.
Change foundBlockChan capacity.
Use lock instead of atomic in the template manager.

* Fix self assignment

* Fix lock

* Fix Dockerfile

* Add comment
2021-02-03 11:53:55 +02:00
Elichai Turkel
e7f9606683 Add dummy go files for test only package, to mitigate golang/go#27333 (#1480)
* Add dummy go files for test only package, to mitigate golang/go#27333

* Stop ignoring errors when producing the coverage

* Add comments explaining the dummy go files

* Make the coverage output non-json
2021-02-02 18:20:15 +02:00
stasatdaglabs
97be133cee Add logs to help debug long virtual parent selection. (#1470)
Co-authored-by: Ori Newman <orinewman1@gmail.com>
2021-02-01 19:15:50 +02:00
Ori Newman
aeb8e9d2cd Unban address after one day (#1479)
* Unban address after one day

* Unban addresses one by one

Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com>
2021-02-01 18:52:49 +02:00
Ori Newman
b636ae234e Add ban and unban RPC commands (#1478)
* Add ban and unban RPC commands

* Fix names

* Fix commands strings

* Update RPC documentation

* Rename functions

* Simplify return

* Use IP strings in app messages

* Add parse IP error

* Fix wrong condition
2021-02-01 17:34:43 +02:00
Elichai Turkel
a3913dbf80 Update to version 0.9.0 2021-02-01 15:39:39 +02:00
Elichai Turkel
2871a6a527 Update to version 0.8.7 2021-02-01 15:38:40 +02:00
Svarog
d5a3a96bde Use hard-coded sample config instead of assumed path (#1466)
* Use hard-coded sample config instead of assumed path

* Fix bad path to sample-kaspad.conf in TestCreateDefaultConfigFile

Co-authored-by: Elichai Turkel <elichai.turkel@gmail.com>
2021-02-01 15:15:37 +02:00
Elichai Turkel
12c438d389 Fix data races in ConnectionManager and flow tests (#1474)
* Reuse the ticker in ConnectionManager.waitTillNextIteration

* Fix a data race in ConnectionManager by locking the mutex

* Add a mutex to fakeRelayInvsContext in block relay flow test

Co-authored-by: Ori Newman <orinewman1@gmail.com>
2021-02-01 15:03:31 +02:00
Elichai Turkel
280fa3de46 Prevent infinite ticker leaks in kaspaminer (#1476)
* Prevent infinite tickers leaks in kaspaminer

* Reset ticker in ConnectionManager instead of allocating a new one

Co-authored-by: Ori Newman <orinewman1@gmail.com>
2021-02-01 14:52:17 +02:00
Elichai Turkel
d281dabdb4 Bump Go version to 1.15 (#1477) 2021-02-01 14:35:11 +02:00
Ori Newman
331042edf1 Add defaultTargetBlocksPerSecond (#1473)
* Add defaultTargetBlocksPerSecond

* Use different default per network
2021-02-01 14:26:45 +02:00
Ori Newman
669a9ab4c3 Ban by IP (#1471)
* Ban by IP

* Fix panic

* Fix error format

* Remove failed addresses

Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com>
2021-02-01 10:51:18 +02:00
stasatdaglabs
65e149b2bb In kaspaminer, don't crash on submitBlock timeout (#1462)
* In kaspaminer, don't crash on submitBlock timeout.

* Make timeout messages have a log level of Warn.

* Wait for a second after receiving a reject for IBD.

Co-authored-by: Elichai Turkel <elichai.turkel@gmail.com>
2021-01-29 09:10:21 +02:00
stasatdaglabs
7c1495ba65 Force stop gRPC servers after a short timeout (#1463)
* Force stop gRPC servers after a short timeout.

* Use spawn instead of go.
2021-01-28 19:43:04 +02:00
Ori Newman
13ffa5093c Increase the waiting for error timeout (#1465) 2021-01-28 13:33:37 +02:00
Ori Newman
a9a810a2b2 Add block type to MineJSON (#1464) 2021-01-28 13:22:20 +02:00
Michael Sutton
c9b591f2d3 Final reindex algorithm (#1430)
* Mine JSON

* [Reindex tests] add test_params and validate_mining flag to test_consensus

* Rename file and extend tests

* Ignore local test datasets

* Use spaces over tabs

* Reindex algorithm - full algorithm, initial commit, some tests fail

* Reindex algorithm - a few critical fixes

* Reindex algorithm - move reindex struct and all related operations to new file

* Reindex algorithm - added a validateIntervals method and modified tests to use it (instead of exact comparisons)

* Reindex algorithm - modified reindexIntervals to receive the new child as argument and fixed an important related bug

* Reindex attack tests - move logic to helper function and add stretch test

* Reindex algorithm - variable names and some comments

* Reindex algorithm - minor changes

* Reindex algorithm - minor changes 2

* Reindex algorithm - extended stretch test

* Reindex algorithm - small fix to validate function

* Reindex tests - move tests and add DAG files

* go format fixes

* TestParams doc comment

* Reindex tests - exact comparisons are not needed

* Update to version 0.8.6

* Remove TestParams and use AddUTXOInvalidHeader instead

* Use gzipeed test files

* This unintended change somehow slipped in through branch merges

* Rename test

* Move interval increase/decrease methods to reachability interval file

* Addressing a bunch of minor review comments

* Addressed a few more minor review comments

* Make code of offsetSiblingsBefore and offsetSiblingsAfter symmetric

* Optimize reindex logic in cases where reorg occurs + reorg test

* Do not change reindex root too fast (on reorg)

* Some comments

* A few more comments

* Addressing review comments

* Remove TestNoAttackAlternateReorg and assert chain attack

* Minor

Co-authored-by: Elichai Turkel <elichai.turkel@gmail.com>
Co-authored-by: Mike Zak <feanorr@gmail.com>
Co-authored-by: Ori Newman <orinewman1@gmail.com>
2021-01-27 17:09:20 +02:00
Ori Newman
8d6e71d490 Add IBD test cases and check for MsgUnexpectedPruningPoint on receivePruningPointBlock as well (#1459)
* Check for MsgUnexpectedPruningPoint on receivePruningPointBlock as well

* Add IBD test cases

* Revert "Check for MsgUnexpectedPruningPoint on receivePruningPointBlock as well"

This reverts commit 6a6d1ea180.

* Change log level for two logs

* Remove "testing a situation where the pruning point moved during IBD (before sending the pruning point block)"
2021-01-27 16:42:42 +02:00
Elichai Turkel
2823461fe2 Change AddressKey from string to port+ipv6 address (#1458) 2021-01-27 16:09:32 +02:00
Elichai Turkel
2075c585da Fix race condition in kaspaminer (#1455)
Co-authored-by: Ori Newman <orinewman1@gmail.com>
2021-01-27 13:08:35 +02:00
Elichai Turkel
01aee62cb0 Add log and measure to pruning points (#1457) 2021-01-27 11:40:51 +02:00
Ori Newman
a6ee871f7e Increase maxSelectedParentTimeDiffToAllowMiningInMilliSeconds to one hour (#1456) 2021-01-27 11:04:58 +02:00
Mike Zak
6393a8186a Update to version 0.9.0 2021-01-27 09:22:56 +02:00
stasatdaglabs
3916534a7e In kaspactl, prettify responses before printing them (#1453)
* In kaspactl, prettify responses before printing them.

* Indent with four spaces instead of a tab.

* Unwrap the response.

* Simplify unwrapping the response.

* Don't unwrap responses.

* Use protojson.MarshalOptions for prettification.
2021-01-27 09:13:48 +02:00
Elichai Turkel
0561347ff1 Remove default dns/grpc seeders (#1454) 2021-01-26 17:40:54 +02:00
Svarog
fb11981da1 Make kaspactl usable by human beings (#1452)
* Add request_types and their help

* Added command parser

* Updated main to use command if it's specified

* Some progress in making everything work

* Fix command parser for pointers to structs

* Cleanup code

* Enhance usage text

* Fixed typo

* Some minor style fixing, and remove temporary code

* Correctly fallthrough in stringToValue unsupported types

Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com>
2021-01-26 14:22:35 +02:00
Ori Newman
1742e76af7 Add TestHandleRelayInvsErrors (#1450) 2021-01-26 13:31:28 +02:00
Svarog
ddfe376388 Don't ban peers that sent a requested duplicate block (#1440)
* Ignore ErrDuplicateBlock for any blocks that were requested

* Don't check for DuplicateBlockErr in processHeaders + improve logs

* Return the check for ErrDuplicateBlock in processHeader

* Fix log message
2021-01-26 08:42:04 +02:00
Elichai Turkel
52da65077a codecov: Ignore protobuf autogenerated files (#1449) 2021-01-25 16:58:11 +02:00
Elichai Turkel
ed85f09742 Add P2P/RPC name to the gRPC logs (#1448)
* Add P2P/RPC name to the gRPC logs

* Add p2p/rpc name in more logs
2021-01-25 16:41:43 +02:00
stasatdaglabs
819ec9f2a7 Pass fromOutpoint into GetPruningPointUTXOs instead of offset, so it could Seek over the cursor (#1447)
* Implement TestGetPruningPointUTXOs.

* Fix a bad error message.

* Fix TestGetPruningPointUTXOs for testnet.

* Make sure all the UTXOs are returned in TestGetPruningPointUTXOs.

* Implement BenchmarkGetPruningPointUTXOs.

* Pass fromOutpoint into GetPruningPointUTXOs instead of offset, so it could Seek over the cursor.

* Fix weird benchmark timer calls.

* Remove unnecessary collection of outpointAndUTXOEntryPairs from BenchmarkGetPruningPointUTXOs.

* Fix a comment.
2021-01-25 13:38:59 +02:00
Ori Newman
7ea8a72a9e Add TestReceiveAddressesErrors (#1446)
* Add TestReceiveAddressesErrors

* Change errors to be more descriptive

* Fix checkFlowError
2021-01-24 17:35:20 +02:00
stasatdaglabs
ca04c049ab When the pruning point moves, update its UTXO set outside of a database transaction (#1444)
* Remove pruningPointUTXOSetStaging and implement UpdatePruningPointUTXOSet.

* Implement StageStartSavingNewPruningPointUTXOSet, HadStartedSavingNewPruningPointUTXOSet, and FinishSavingNewPruningPointUTXOSet.

* Fix a bad return.

* Implement UpdatePruningPointUTXOSetIfRequired.

* Call UpdatePruningPointUTXOSetIfRequired on consensus creation.

* Rename savingNewPruningPointUTXOSetKey to updatingPruningPointUTXOSet.

* Add a log.

* Add calls to runtime.GC() at its start and end.

* Rename a variable.

* Replace calls to runtime.GC to calls to LogMemoryStats.

* Wrap the contents of LogMemoryStats in a log closure.
2021-01-24 14:48:11 +02:00
Ori Newman
9a17198e7d Remove redundant type check (#1445) 2021-01-24 14:14:03 +02:00
stasatdaglabs
756f40c59a Sync pruning point UTXO sets incrementally instead of all at once (#1431)
* Replaced the content of MsgIBDRootUTXOSetChunk with pairs of outpoint-utxo entry pairs.

* Rename utxoIter to utxoIterator.

* Add a big stinky TODO on an assert.

* Replace pruningStore staging with a UTXO set iterator.

* Reimplement receiveAndInsertIBDRootUTXOSet.

* Extract OutpointAndUTXOEntryPairsToDomainOutpointAndUTXOEntryPairs into domainconverters.go.

* Pass the outpoint and utxy entry pairs to the pruning store.

* Implement InsertCandidatePruningPointUTXOs.

* Implement ClearCandidatePruningPointUTXOs.

* Implement UpdateCandidatePruningPointMultiset.

* Use the candidate pruning point multiset in updatePruningPoint.

* Implement CandidatePruningPointUTXOIterator.

* Use the pruning point utxo set iterator for StageVirtualUTXOSet.

* Defer ClearCandidatePruningPointUTXOs.

* Implement OverwriteVirtualUTXOSet.

* Implement CommitCandidatePruningPointUTXOSet.

* Implement BeginOverwritingVirtualUTXOSet and FinishOverwritingVirtualUTXOSet.

* Implement overwriteVirtualUTXOSetAndCommitPruningPointUTXOSet.

* Rename ClearCandidatePruningPointUTXOs to ClearCandidatePruningPointData.

* Add missing methods to dbManager.

* Implement PruningPointUTXOs.

* Implement RecoverUTXOIfRequired.

* Delete the utxoserialization package.

* Fix compilation errors in TestValidateAndInsertPruningPoint.

* Switch order of operations in the if statements in PruningPointUTXOs so that Next() wouldn't be unnecessarily called.

* Fix missing pruning point utxo set staging and bad slice length.

* Fix no default multiset in InsertCandidatePruningPointUTXOs.

* Make go vet happy.

* Rename candidateXXX to importedXXX.

* Do some more renaming.

* Rename some more.

* Fix bad MsgIBDRootNotFound logic.

* Fix an error message.

* Simplify receiveIBDRootBlock.

* Fix error message in receiveAndInsertIBDRootUTXOSet.

* Do some more renaming.

* Fix merge errors.

* Fix a bug caused by calling iterator.First() unnecessarily.

* Remove databaseContext from stores and don't use a transaction in ClearXXX functions.

* Simplify receiveAndInsertIBDRootUTXOSet.

* Fix offset count in PruningPointUTXOs().

* Fix readOnlyUTXOIteratorWithDiff.First().

* Split handleRequestIBDRootUTXOSetAndBlockFlow into smaller methods.

* Rename IbdRootNotFound to UnexpectedPruningPoint.

* Rename requestIBDRootHash to requestPruningPointHash.

* Rename IBDRootHash to PruningPointHash.

* Rename RequestIBDRootUTXOSetAndBlock to RequestPruningPointUTXOSetAndBlock.

* Rename IBDRootUTXOSetChunk to PruningPointUTXOSetChunk.

* Rename RequestNextIBDRootUTXOSetChunk to RequestNextPruningPointUTXOSetChunk.

* Rename DoneIBDRootUTXOSetChunks to DonePruningPointUTXOSetChunks.

* Rename remaining references to IBD root.

* Fix an error message.

* Add a check for HadStartedImportingPruningPointUTXOSet in commitVirtualUTXODiff.

* Add a check for HadStartedImportingPruningPointUTXOSet in ImportPruningPointUTXOSetIntoVirtualUTXOSet.

* Move FinishImportingPruningPointUTXOSet closer to HadStartedImportingPruningPointUTXOSet.

* Remove reference to pruningStore in utxoSetIterator.

* Pointerify utxoSetIterator receivers.

* Fix bad insert in CommitImportedPruningPointUTXOSet.

* Rename commitImportedPruningPointUTXOSetAll to applyImportedPruningPointUTXOSet.

* Simplify PruningPointUTXOs.

* Add populateTransactionWithUTXOEntriesFromUTXOSet.

* Fix a TODO comment.

* Rename InsertImportedPruningPointUTXOs to AppendImportedPruningPointUTXOs.

* Extract handleRequestPruningPointUTXOSetAndBlockMessage to a separate method.

* Rename stuff in readOnlyUTXOIteratorWithDiff.First().

* Address toAddIterator in readOnlyUTXOIteratorWithDiff.First().

* Call First() before any full iteration on ReadOnlyUTXOSetIterator.

* Call First() before any full iteration on a database Cursor.

* Put StartImportingPruningPointUTXOSet inside the pruning point transaction.

* Make serializeOutpoint and serializeUTXOEntry free functions in pruningStore.

* Fix readOnlyUTXOIteratorWithDiff.First().

* Fix bad validations in importPruningPoint.

* Remove superfluous call to validateBlockTransactionsAgainstPastUTXO.
2021-01-21 17:24:52 +02:00
Elichai Turkel
6a03d31f98 Remove accidental pointer indirection in dbKey (#1441) 2021-01-21 12:14:52 +02:00
Ori Newman
319ab6cfcd Always request orphan roots, even when you get an inv of a known orphan (#1436)
Co-authored-by: Svarog <feanorr@gmail.com>
2021-01-20 10:58:45 +02:00
Ori Newman
abef96e3de Add TestIBDWithPruning (#1425)
* Add TestIBDWithPruning

* Test block count

* Fix a typo

Co-authored-by: Elichai Turkel <elichai.turkel@gmail.com>
Co-authored-by: Mike Zak <feanorr@gmail.com>
2021-01-20 10:07:32 +02:00
stasatdaglabs
2e0bc0f8c4 Increase P2P connections' dial timeouts to 5 seconds (#1437)
Co-authored-by: Elichai Turkel <elichai.turkel@gmail.com>
2021-01-20 09:48:48 +02:00
Ori Newman
acf5423c63 Remove docker test parallelism (#1434)
* Remove docker parallelism

* Remove redundant dash
2021-01-19 17:30:24 +02:00
Ori Newman
effb545d20 Fix wrong condition and add logs (#1435) 2021-01-19 17:20:25 +02:00
Svarog
ad9c213a06 Restructure database to prevent double-slashes in keys, causing bugs in cursors (#1432)
* Add TestValidateAndInsertPruningPointWithSideBlocks

* Optimize infrastracture bucket paths

* Update infrastracture tests

* Refactor the consensus/database layer

* Remove utils/dbkeys

* Use consensus/database in consensus instead of infrastructure

* Fix a bug in dbBucketToDatabaseBucket and MakeBucket combination

Co-authored-by: Elichai Turkel <elichai.turkel@gmail.com>
Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com>
2021-01-19 14:19:08 +02:00
Svarog
a4adbabf96 TestBuildBlockErrorCases and remove redundant check of coinbase script length (#1427)
* Write structure of TestBlockBuilderErrorCases

* Remove double verification of script length in serializeCoinbasePayload

* Remove redundant code in TestBuildBlockErrorCases

* Rename povTransactionHash -> povBlockHash

* Convert coinbasePayloadScriptPublicKeyMaxLength to uint8

* Re-use consensus in TestBuildBlockErrorCases
2021-01-19 10:37:51 +02:00
Ori Newman
799eb7515c Test validateAndInsertPruningPoint (#1420)
* Add TestValidateAndInsertPruningPoint

* Check fake UTXO set and validate that the pruning point changed
2021-01-18 18:17:13 +02:00
Mike Zak
0769705b37 Update to version 0.8.6 2021-01-18 15:17:15 +02:00
Svarog
189e3b6be9 Fix missing utxo notifications (#1428)
* fix missing UTXO notifications (#1426)

* Remove redundant semicolon

Co-authored-by: aspect <anton.yemelyanov@gmail.com>
2021-01-18 13:08:16 +02:00
Mike Zak
e8dfbc8367 Merge remote-tracking branch 'origin/master' into v0.8.5-dev 2021-01-18 11:36:25 +02:00
Ori Newman
d70740331a Remove hashesQueueSet (#1424)
Co-authored-by: Svarog <feanorr@gmail.com>
2021-01-18 09:10:26 +02:00
Svarog
9a81b1328a Add the Address of node to whom connected in log of send/receiveVersion (#1423)
* Add the Address of node to whom connected in log of send/receiveVersion

* Don't call functions before LogAndMeasureExecutionTime

Co-authored-by: Ori Newman <orinewman1@gmail.com>
2021-01-17 16:31:48 +02:00
Ori Newman
d4f3a252ff Add TestIsFinalizedTransaction (#1422)
Co-authored-by: Svarog <feanorr@gmail.com>
2021-01-17 15:47:49 +02:00
Ori Newman
f14527de4c Give different limit to the RPC server (#1421) 2021-01-17 13:58:42 +02:00
Ori Newman
dd57e6abe6 Fix checkParentHeadersExist and cover pruning_violation_proof_of_work_and_difficulty.go with tests (#1418)
* Fix checkParentHeadersExist and cover pruning_violation_proof_of_work_and_difficulty.go with tests

* Remove unused variable

* Change consensus violation

* Change condition order

* Get rid of irrelevant error codes in extractRejectCode

* Fix wrong test db names

* Fix checkParentHeadersExist
2021-01-17 11:27:04 +02:00
Ori Newman
67be4d82bf Don't mark bad merkle root as invalid (#1419)
* Don't mark bad merkle root as invalid

* Fix TestBlockStatus

* Move discardAllChanges inside the inner if
2021-01-17 10:40:05 +02:00
Ori Newman
a1381d6768 Add TestCheckParentBlockBodiesExist (#1405)
* Add TestCheckParentBlockBodiesExist

* Use block in pruning point's anticone for the test

* Fix test db name
2021-01-14 13:31:17 +02:00
Ori Newman
10b519a3e2 Add tests to ValidateHeaderInIsolation (#1415)
* Add tests to ValidateHeaderInIsolation

* Fix tests db names

Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com>
2021-01-14 11:06:08 +02:00
Ori Newman
a35f8269ea Add checkBlockIsNotPruned (#1413)
* Add checkBlockIsNotPruned

* Fix test name and comment

Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com>
2021-01-14 10:56:22 +02:00
stasatdaglabs
15af6641fc Send the IBD root UTXO set in chunks instead of a massive monolythic message (#1412)
* Extract syncPruningPointUTXOSet to a separate method.

* Implement logic to send pruning point utxo set chunks in a loop.

* Replace IBDRootUTXOSetAndBlockMessage with IbdRootUtxoSetChunkMessage.

* Add a new message: RequestNextIBDRootUTXOSetChunk.

* Add a new message: DoneIBDRootUTXOSetChunks.

* Protect HandleRequestIBDRootUTXOSetAndBlock from rogue messages.

* Reimplement receiveIBDRootUTXOSetAndBlock.

* Add CmdDoneIBDRootUTXOSetChunks to the HandleRelayInvs flow.

* Decrease the max message size to 10mb.

* Fix bad step.

* Fix confusion between outgoing/incoming routes.

* Measure how long it takes to send/receive the UTXO set.

* Use LogAndMeasure in handleRequestIBDRootUTXOSetAndBlockFlow.
2021-01-13 18:03:07 +02:00
Svarog
1b97cfb302 Prevent a race condition in findHighestSharedBlockHash where we get headersSelectedTip and then pass it as highHash to GetBlockLocator, without locking consensus (#1410)
* Prevent a race condition in findHighestSharedBlockHash where we get headersSelectedTip and then pass it as highHash to GetBlockLocator, without locking consensus

* Restart findHighestSharedBlockHash if lowHash or highHash are no longer in selectedParentChain

* Test for specifically ErrBlockNotInSelectedParentChain instead of database NotFound error

* Fix TestCreateHeadersSelectedChainBlockLocator

Co-authored-by: Ori Newman <orinewman1@gmail.com>
2021-01-13 17:55:37 +02:00
Ori Newman
61be80a60c Add TestCheckMergeSizeLimit (#1408)
Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com>
2021-01-13 16:19:52 +02:00
Elichai Turkel
83134cc2b5 Add a codecov yml, disable patch checks and make status checks always pass (#1414) 2021-01-13 15:57:57 +02:00
Svarog
4988817da1 Reject SubmitBlock if the node is in IBD (#1409)
* Reject SubmitBlock if the node is in IBD

* Add comments

* Don't use iota for RejectReason constants, since in .proto those are hard-coded
2021-01-13 15:04:55 +02:00
Elichai Turkel
68bd8330ac Log the networks hashrate (#1406)
* Log the hashrate of each block

* Add a test for GetHashrateString

* Move difficulty related functions to its own package

* Convert the validated log in validateAndInsertBlock to a log function

* Add tests for max/min int
2021-01-13 12:51:23 +02:00
Elichai Turkel
192dd2ba8f Add codecov to github (#1358) 2021-01-13 10:35:12 +02:00
Ori Newman
cc49b1826a Reset windowExpectedEndTime after each window (#1407) 2021-01-12 21:34:26 +02:00
Svarog
ce348373c6 Delete existing UTXOSet when commiting VirtualUTXOSet. (#1403) 2021-01-12 16:51:56 +02:00
talelbaz
8ad5725421 Adding a test for the error cases on the function 'checkBlockStatus()' (#1398)
* Adds test for error cases on the function checkBlockStatus.

* Fix review's comments.

* Move test to validateandinsertblock_test.go

Co-authored-by: tal <tal@daglabs.com>
Co-authored-by: Ori Newman <orinewman1@gmail.com>
2021-01-12 15:55:02 +02:00
Ori Newman
23a2fbf401 Remove erroneous finality optimization from LowestChainBlockAboveOrEqualToBlueScore (#1402)
* Remove finality erroneous optimization from LowestChainBlockAboveOrEqualToBlueScore

* Add TestLowestChainBlockAboveOrEqualToBlueScore

* Remove unnecessary fields from dagTraversalManager
2021-01-12 15:27:08 +02:00
Elichai Turkel
c1361e5b3e Change log sizes and add some new features to logger (#1400)
* Increase default log sizes, and increase kaspad log sizes

* Add an option to not print logs to stdout

* Allow logs to be printed in the current working directory

* Add more pruning related logs

* Add comment and increase log rotations to save last 64 logs
2021-01-12 13:26:29 +02:00
Ori Newman
53744ceb45 Compare transaction IDs with Equal (#1401) 2021-01-12 12:53:33 +02:00
Ori Newman
bcf2302460 Add high hash to block locator, and add block locator tests (#1397)
* Include high hash in the block locator

* Add tests for block locator

* Remove redundant function

* Remove redundant assignments
2021-01-12 11:16:25 +02:00
Ori Newman
6101e6bdb6 Fix UTXO serialization, its test, and the static check that missed it (#1396)
* Fix UTXO serialization, its test, and the static check that missed it

* Remove duplicate case

* Use one line for static check

Co-authored-by: Elichai Turkel <elichai.turkel@gmail.com>
2021-01-11 17:45:17 +02:00
stasatdaglabs
d9b97afb92 Don't swallow errors in HandleNewBlockTransactions. (#1390)
Co-authored-by: Ori Newman <orinewman1@gmail.com>
2021-01-11 17:16:15 +02:00
Ori Newman
b8ca33d91d Add selected chain store and optimize block locator with it (#1394)
* Add selected chain store and optimize block locator with it

* Fix build error

* Fix comments

* Fix IsStaged

* Rename CalculateSelectedParentChainChanges to CalculateChainPath and SelectedParentChainChanges->SelectedChainPath

* Use binary.LittleEndian directly to allow compiler optimizations

* Remove boolean from HeadersSelectedChainStore interface

* Prevent endless loop in block locator
2021-01-11 15:51:45 +02:00
Svarog
c7deda41c6 Fix deserialization of script version in UTXOSet deserialization (#1395)
* Initalize protoUTXOSetIterator with index = -1

* Handle error when failed to deserialize Script version

* Add support for (de)serialization of (u)int16

* Log the error when converting it into ErrMalformedUTXO
2021-01-11 15:23:27 +02:00
talelbaz
434cf45112 Adds a new test to validate POW, and Fix Main-net and Test-net genesis block data. (#1389)
* commit for do fetch&merge

* Adds a new test to validate POW, and Fix Main-net and Testnet genesis block data.

* Fix window's test for testnet and change the expected pruning point for mainnet and testnet.

* Delete function "solveBlock" on proof_of_work_test.go and call the function mining.SolvaBlock instead. Also, remove using of random in "solveBlockWithWrongPOW" function.

* Replace 0xFFFFFFFFFFFFFFFF to math.MaxUint64 in "solveBlockWithWrongPOW" function and change the function's comment of "TestPOW"

* Replace 0xFFFFFFFFFFFFFFFF to math.MaxUint64 in "solveBlockWithWrongPOW" function and change the function's comment of "TestPOW"

* Change from <= to < in the for statement in "solveBlockWithWrongPOW" function

* Adds one arg to the function call "NewTestConsensus" (the function sig has changed).

Co-authored-by: tal <tal@daglabs.com>
Co-authored-by: Ori Newman <orinewman1@gmail.com>
2021-01-11 13:15:26 +02:00
Elichai Turkel
2cc0bf1639 Optimize block locator using finality store (#1386)
* Make sure block locator doesn't include a hash lower than the lowHash in
the block locator

* Use finalityStore to optimize LowestChainBlockAboveOrEqualToBlueScore
2021-01-10 13:36:02 +02:00
Ori Newman
0f2d0d45b5 Add TargetBlocksPerSecond for kaspaminer (#1385)
Co-authored-by: Svarog <feanorr@gmail.com>
2021-01-10 12:44:24 +02:00
Svarog
09e1a73340 Added some logs to block-relay and IBD flows (#1384)
Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com>
2021-01-10 12:05:34 +02:00
Svarog
c6d20c1f6f Start IBDBlockLocator from PruningPoint instead of Genesis (#1383) 2021-01-10 11:06:06 +02:00
Svarog
49e0a2a2e7 Add basic support for archival node (#1370)
* Add archival cli flag

* If --archival was activated - don't delete anything

* Fix tests

* Still change block status to StatusHeaderOnly even in archival nodes
2021-01-10 10:25:15 +02:00
Ori Newman
285ae5cd40 Update READMEs and add CONTRIBUTING.md (#1381)
* Update READMEs and add CONTRIBUTING.md

* Update go version

* Update READMEs and CONTRIBUTING.md

* Update README.md

* Update README.md

* Update README.md
2021-01-10 09:13:00 +02:00
stasatdaglabs
541205904e Add RPC documentation (#1379)
* Split messages.proto to p2p and rpc.

* Split messages.proto to p2p and rpc.

* Write a short intro to the RPC docs.

* Start documenting RPC calls.

* Use a custom protoc-gen-doc.

* Continue writing RPC documentation.

* Finish writing RPC documentation.

* Fix a formatting error.

* Fix merge errors.

* Fix formatting into protowire/README.md.

* Rerun go generate ..

Co-authored-by: Ori Newman <orinewman1@gmail.com>
2021-01-07 16:55:47 +02:00
Ori Newman
256b7f25f1 Decrease grpc client dial timeout to one second (#1378) 2021-01-07 16:26:37 +02:00
Elichai Turkel
82d95c59a3 Increase log files sizes (#1374) 2021-01-07 11:17:13 +02:00
Ori Newman
79c5d4595e Remove gencerts (#1371) 2021-01-07 10:00:13 +02:00
stasatdaglabs
b195301a99 Add IsIBDPeer to GetConnectedPeerInfoResponse. (#1367)
Co-authored-by: Ori Newman <orinewman1@gmail.com>
2021-01-06 17:33:51 +02:00
Ori Newman
4ad89056c2 Implement getMempoolEntries (#1369)
* Implement getMempoolEntries

* Fix comment

* Fix comment
2021-01-06 17:26:21 +02:00
Elichai Turkel
64f6cd2178 Merge pull request #1368 from kaspanet/v0.8.4-dev
Upgrade master to 0.8.4rc0
2021-01-06 14:43:39 +02:00
Mike Zak
26368cd674 Update to version 0.8.5 2021-01-06 14:04:46 +02:00
alexandratran
f6dfce8180 Update README.md 2020-11-26 21:46:57 -08:00
Yuval Shaul
a361d62945 Merge remote-tracking branch 'origin/v0.6.2-dev' 2020-08-16 13:51:25 +03:00
Yuval Shaul
aac173ed72 Merge remote-tracking branch 'origin/v0.6.1-dev' 2020-08-12 10:48:51 +03:00
stasatdaglabs
5f3fb0bf9f [NOD-1238] Fix acceptance index never being initialized. (#859) 2020-08-11 12:04:54 +03:00
Mike Zak
61f383a713 Merge remote-tracking branch 'origin/v0.6.0-dev' 2020-08-09 09:09:27 +03:00
Mike Zak
c62bdb2fa1 Merge remote-tracking branch 'origin/v0.5.0-dev' 2020-07-01 15:05:02 +03:00
Svarog
c88869778d [NOD-869] Add a print after os.Exit(1) to see if it is ever called (#701) 2020-04-22 11:37:30 +03:00
Ori Newman
3fd647b291 [NOD-858] Don't switch sync peer if the syncing process hasn't yet started with the current sync peer (#700)
* [NOD-858] Don't switch sync peer if the syncing process hasn't yet started with the current sync peer

* [NOD-858] SetShouldSendBlockLocator(false) on OnBlockLocator

* [NOD-858] Rename shouldSendBlockLocator->wasBlockLocatorRequested

* [NOD-858] Move panic to shouldReplaceSyncPeer
2020-04-13 15:49:46 +03:00
Mike Zak
2f255952b7 Updated to version v0.3.1 2020-04-13 15:10:27 +03:00
512 changed files with 31440 additions and 17400 deletions

9
.codecov.yml Normal file
View File

@@ -0,0 +1,9 @@
coverage:
status:
patch: off
project:
default:
informational: true
ignore:
- "**/*.pb.go" # Ignore all auto generated protobuf structures.

68
.github/workflows/go-deploy.yml vendored Normal file
View File

@@ -0,0 +1,68 @@
name: Build and Upload assets
on:
release:
types: [published]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest, windows-latest, macos-latest ]
name: Building For ${{ matrix.os }}
steps:
- name: Fix windows CRLF
run: git config --global core.autocrlf false
- name: Check out code into the Go module directory
uses: actions/checkout@v2
# We need to increase the page size because the tests run out of memory on github CI windows.
# Use the powershell script from this github action: https://github.com/al-cheb/configure-pagefile-action/blob/master/scripts/SetPageFileSize.ps1
# MIT License (MIT) Copyright (c) 2020 Maxim Lobanov and contributors
- name: Increase page size on windows
if: runner.os == 'Windows'
shell: powershell
run: powershell -command .\.github\workflows\SetPageFileSize.ps1
- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: 1.16
- name: Build on linux
if: runner.os == 'Linux'
# `-extldflags=-static` - means static link everything, `-tags netgo,osusergo` means use pure go replacements for "os/user" and "net"
# `-s -w` strips the binary to produce smaller size binaries
run: |
binary="kaspad-${{ github.event.release.tag_name }}-linux"
echo "binary=${binary}" >> $GITHUB_ENV
go build -v -ldflags="-s -w -extldflags=-static" -tags netgo,osusergo -o "${binary}"
- name: Build on Windows
if: runner.os == 'Windows'
shell: bash
run: |
binary="kaspad-${{ github.event.release.tag_name }}-win64.exe"
echo "binary=${binary}" >> $GITHUB_ENV
go build -v -ldflags="-s -w" -o "${binary}"
- name: Build on MacOS
if: runner.os == 'macOS'
run: |
binary="kaspad-${{ github.event.release.tag_name }}-osx"
echo "binary=${binary}" >> $GITHUB_ENV
go build -v -ldflags="-s -w" -o "${binary}"
- name: Upload Release Asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: "./${{ env.binary }}"
asset_name: "${{ env.binary }}"
asset_content_type: application/zip

49
.github/workflows/go-race.yml vendored Normal file
View File

@@ -0,0 +1,49 @@
name: Go-Race
on:
schedule:
- cron: "0 0 * * *"
workflow_dispatch:
jobs:
race_test:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
branch: [ master, latest ]
name: Race detection on ${{ matrix.branch }}
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: 1.15
- name: Set scheduled branch name
shell: bash
if: github.event_name == 'schedule'
run: |
if [ "${{ matrix.branch }}" == "master" ]; then
echo "run_on=master" >> $GITHUB_ENV
fi
if [ "${{ matrix.branch }}" == "latest" ]; then
branch=$(git branch -r | grep 'v\([0-9]\+\.\)\([0-9]\+\.\)\([0-9]\+\)-dev' | sort -Vr | head -1 | xargs)
echo "run_on=${branch}" >> $GITHUB_ENV
fi
- name: Set manual branch name
shell: bash
if: github.event_name == 'workflow_dispatch'
run: echo "run_on=${{ github.ref }}" >> $GITHUB_ENV
- name: Test with race detector
shell: bash
run: |
git checkout "${{ env.run_on }}"
git status
go test -race ./...

View File

@@ -11,6 +11,7 @@ jobs:
build: build:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
fail-fast: false
matrix: matrix:
os: [ ubuntu-16.04, macos-10.15 ] os: [ ubuntu-16.04, macos-10.15 ]
name: Testing on on ${{ matrix.os }} name: Testing on on ${{ matrix.os }}
@@ -34,7 +35,7 @@ jobs:
- name: Set up Go 1.x - name: Set up Go 1.x
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.14 go-version: 1.16
# Source: https://github.com/actions/cache/blob/main/examples.md#go---modules # Source: https://github.com/actions/cache/blob/main/examples.md#go---modules
@@ -48,4 +49,22 @@ jobs:
- name: Test - name: Test
shell: bash shell: bash
run: ./build_and_test.sh run: ./build_and_test.sh -v
coverage:
runs-on: ubuntu-20.04
name: Produce code coverage
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: 1.16
- name: Create coverage file
run: go test -v -covermode=atomic -coverpkg=./... -coverprofile coverage.txt ./...
- name: Upload coverage file
run: bash <(curl -s https://codecov.io/bash)

19
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,19 @@
# Contributing to Kaspad
Any contribution to Kaspad is very welcome.
## Getting started
If you want to start contributing to Kaspad and don't know where to start, you can pick an issue from
the [list](https://github.com/kaspanet/kaspad/issues).
If you want to make a big change it's better to discuss it first by opening an issue or talk about it in
[Discord](https://discord.gg/WmGhhzk) to avoid duplicate work.
## Pull Request process
Any pull request should be opened against the development branch of the target version. The development branch format is
as follows: `vx.y.z-dev`, for example: `v0.8.5-dev`.
All pull requests should pass the checks written in `build_and_test.sh`, so it's recommended to run this script before
submitting your PR.

View File

@@ -9,12 +9,16 @@ Warning: This is pre-alpha software. There's no guarantee anything works.
Kaspad is the reference full node Kaspa implementation written in Go (golang). Kaspad is the reference full node Kaspa implementation written in Go (golang).
This project is currently under active development and is in a pre-Alpha state. This project is currently under active development and is in a pre-Alpha state.
Some things still don't work and APIs are far from finalized. The code is provided for reference only. Some things still don't work and APIs are far from finalized. The code is provided for reference only.
## What is kaspa
Kaspa is an attempt at a proof-of-work cryptocurrency with instant confirmations and sub-second block times. It is based on [the PHANTOM protocol](https://eprint.iacr.org/2018/104.pdf), a generalization of Nakamoto consensus.
## Requirements ## Requirements
Latest version of [Go](http://golang.org) (currently 1.13). Go 1.16 or later.
## Installation ## Installation
@@ -27,23 +31,17 @@ Latest version of [Go](http://golang.org) (currently 1.13).
```bash ```bash
$ go version $ go version
$ go env GOROOT GOPATH
``` ```
NOTE: The `GOROOT` and `GOPATH` above must not be the same path. It is
recommended that `GOPATH` is set to a directory in your home directory such as
`~/dev/go` to avoid write permission issues. It is also recommended to add
`$GOPATH/bin` to your `PATH` at this point.
- Run the following commands to obtain and install kaspad including all dependencies: - Run the following commands to obtain and install kaspad including all dependencies:
```bash ```bash
$ git clone https://github.com/kaspanet/kaspad $GOPATH/src/github.com/kaspanet/kaspad $ git clone https://github.com/kaspanet/kaspad
$ cd $GOPATH/src/github.com/kaspanet/kaspad $ cd kaspad
$ go install . ./cmd/... $ go install . ./cmd/...
``` ```
- Kaspad (and utilities) should now be installed in `$GOPATH/bin`. If you did - Kaspad (and utilities) should now be installed in `$(go env GOPATH)/bin`. If you did
not already add the bin directory to your system path during Go installation, not already add the bin directory to your system path during Go installation,
you are encouraged to do so now. you are encouraged to do so now.
@@ -53,10 +51,8 @@ $ go install . ./cmd/...
Kaspad has several configuration options available to tweak how it runs, but all Kaspad has several configuration options available to tweak how it runs, but all
of the basic operations work with zero configuration. of the basic operations work with zero configuration.
#### Linux/BSD/POSIX/Source
```bash ```bash
$ ./kaspad $ kaspad
``` ```
## Discord ## Discord
@@ -69,9 +65,8 @@ is used for this project.
## Documentation ## Documentation
The documentation is a work-in-progress. It is located in the [docs](https://github.com/kaspanet/kaspad/tree/master/docs) folder. The [documentation](https://github.com/kaspanet/docs) is a work-in-progress
## License ## License
Kaspad is licensed under the copyfree [ISC License](https://choosealicense.com/licenses/isc/). Kaspad is licensed under the copyfree [ISC License](https://choosealicense.com/licenses/isc/).

View File

@@ -7,21 +7,21 @@ import (
"runtime" "runtime"
"time" "time"
"github.com/kaspanet/kaspad/infrastructure/config"
"github.com/kaspanet/kaspad/infrastructure/db/database" "github.com/kaspanet/kaspad/infrastructure/db/database"
"github.com/kaspanet/kaspad/infrastructure/db/database/ldb" "github.com/kaspanet/kaspad/infrastructure/db/database/ldb"
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/infrastructure/os/signal"
"github.com/kaspanet/kaspad/util/profiling"
"github.com/kaspanet/kaspad/version"
"github.com/kaspanet/kaspad/util/panics"
"github.com/kaspanet/kaspad/infrastructure/config"
"github.com/kaspanet/kaspad/infrastructure/os/execenv" "github.com/kaspanet/kaspad/infrastructure/os/execenv"
"github.com/kaspanet/kaspad/infrastructure/os/limits" "github.com/kaspanet/kaspad/infrastructure/os/limits"
"github.com/kaspanet/kaspad/infrastructure/os/signal"
"github.com/kaspanet/kaspad/infrastructure/os/winservice" "github.com/kaspanet/kaspad/infrastructure/os/winservice"
"github.com/kaspanet/kaspad/util/panics"
"github.com/kaspanet/kaspad/util/profiling"
"github.com/kaspanet/kaspad/version"
) )
const leveldbCacheSizeMiB = 256
var desiredLimits = &limits.DesiredLimits{ var desiredLimits = &limits.DesiredLimits{
FileLimitWant: 2048, FileLimitWant: 2048,
FileLimitMin: 1024, FileLimitMin: 1024,
@@ -49,6 +49,7 @@ func StartApp() error {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
return err return err
} }
defer logger.BackendLog.Close()
defer panics.HandlePanic(log, "MAIN", nil) defer panics.HandlePanic(log, "MAIN", nil)
app := &kaspadApp{cfg: cfg} app := &kaspadApp{cfg: cfg}
@@ -181,5 +182,5 @@ func removeDatabase(cfg *config.Config) error {
func openDB(cfg *config.Config) (database.Database, error) { func openDB(cfg *config.Config) (database.Database, error) {
dbPath := databasePath(cfg) dbPath := databasePath(cfg)
log.Infof("Loading database from '%s'", dbPath) log.Infof("Loading database from '%s'", dbPath)
return ldb.NewLevelDB(dbPath) return ldb.NewLevelDB(dbPath, leveldbCacheSizeMiB)
} }

View File

@@ -3,6 +3,7 @@ package appmessage
import ( import (
"encoding/hex" "encoding/hex"
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader" "github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks" "github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
@@ -163,11 +164,7 @@ func outpointToDomainOutpoint(outpoint *Outpoint) *externalapi.DomainOutpoint {
func RPCTransactionToDomainTransaction(rpcTransaction *RPCTransaction) (*externalapi.DomainTransaction, error) { func RPCTransactionToDomainTransaction(rpcTransaction *RPCTransaction) (*externalapi.DomainTransaction, error) {
inputs := make([]*externalapi.DomainTransactionInput, len(rpcTransaction.Inputs)) inputs := make([]*externalapi.DomainTransactionInput, len(rpcTransaction.Inputs))
for i, input := range rpcTransaction.Inputs { for i, input := range rpcTransaction.Inputs {
transactionIDBytes, err := hex.DecodeString(input.PreviousOutpoint.TransactionID) transactionID, err := transactionid.FromString(input.PreviousOutpoint.TransactionID)
if err != nil {
return nil, err
}
transactionID, err := transactionid.FromBytes(transactionIDBytes)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -197,19 +194,11 @@ func RPCTransactionToDomainTransaction(rpcTransaction *RPCTransaction) (*externa
} }
} }
subnetworkIDBytes, err := hex.DecodeString(rpcTransaction.SubnetworkID) subnetworkID, err := subnetworks.FromString(rpcTransaction.SubnetworkID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
subnetworkID, err := subnetworks.FromBytes(subnetworkIDBytes) payloadHash, err := externalapi.NewDomainHashFromString(rpcTransaction.PayloadHash)
if err != nil {
return nil, err
}
payloadHashBytes, err := hex.DecodeString(rpcTransaction.PayloadHash)
if err != nil {
return nil, err
}
payloadHash, err := externalapi.NewDomainHashFromByteSlice(payloadHashBytes)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -254,7 +243,7 @@ func DomainTransactionToRPCTransaction(transaction *externalapi.DomainTransactio
ScriptPublicKey: &RPCScriptPublicKey{Script: scriptPublicKey, Version: output.ScriptPublicKey.Version}, ScriptPublicKey: &RPCScriptPublicKey{Script: scriptPublicKey, Version: output.ScriptPublicKey.Version},
} }
} }
subnetworkID := hex.EncodeToString(transaction.SubnetworkID[:]) subnetworkID := transaction.SubnetworkID.String()
payloadHash := transaction.PayloadHash.String() payloadHash := transaction.PayloadHash.String()
payload := hex.EncodeToString(transaction.Payload) payload := hex.EncodeToString(transaction.Payload)
return &RPCTransaction{ return &RPCTransaction{
@@ -268,3 +257,49 @@ func DomainTransactionToRPCTransaction(transaction *externalapi.DomainTransactio
Payload: payload, Payload: payload,
} }
} }
// OutpointAndUTXOEntryPairsToDomainOutpointAndUTXOEntryPairs converts
// OutpointAndUTXOEntryPairs to domain OutpointAndUTXOEntryPairs
func OutpointAndUTXOEntryPairsToDomainOutpointAndUTXOEntryPairs(
outpointAndUTXOEntryPairs []*OutpointAndUTXOEntryPair) []*externalapi.OutpointAndUTXOEntryPair {
domainOutpointAndUTXOEntryPairs := make([]*externalapi.OutpointAndUTXOEntryPair, len(outpointAndUTXOEntryPairs))
for i, outpointAndUTXOEntryPair := range outpointAndUTXOEntryPairs {
domainOutpointAndUTXOEntryPairs[i] = &externalapi.OutpointAndUTXOEntryPair{
Outpoint: &externalapi.DomainOutpoint{
TransactionID: outpointAndUTXOEntryPair.Outpoint.TxID,
Index: outpointAndUTXOEntryPair.Outpoint.Index,
},
UTXOEntry: utxo.NewUTXOEntry(
outpointAndUTXOEntryPair.UTXOEntry.Amount,
outpointAndUTXOEntryPair.UTXOEntry.ScriptPublicKey,
outpointAndUTXOEntryPair.UTXOEntry.IsCoinbase,
outpointAndUTXOEntryPair.UTXOEntry.BlockBlueScore,
),
}
}
return domainOutpointAndUTXOEntryPairs
}
// DomainOutpointAndUTXOEntryPairsToOutpointAndUTXOEntryPairs converts
// domain OutpointAndUTXOEntryPairs to OutpointAndUTXOEntryPairs
func DomainOutpointAndUTXOEntryPairsToOutpointAndUTXOEntryPairs(
outpointAndUTXOEntryPairs []*externalapi.OutpointAndUTXOEntryPair) []*OutpointAndUTXOEntryPair {
domainOutpointAndUTXOEntryPairs := make([]*OutpointAndUTXOEntryPair, len(outpointAndUTXOEntryPairs))
for i, outpointAndUTXOEntryPair := range outpointAndUTXOEntryPairs {
domainOutpointAndUTXOEntryPairs[i] = &OutpointAndUTXOEntryPair{
Outpoint: &Outpoint{
TxID: outpointAndUTXOEntryPair.Outpoint.TransactionID,
Index: outpointAndUTXOEntryPair.Outpoint.Index,
},
UTXOEntry: &UTXOEntry{
Amount: outpointAndUTXOEntryPair.UTXOEntry.Amount(),
ScriptPublicKey: outpointAndUTXOEntryPair.UTXOEntry.ScriptPublicKey(),
IsCoinbase: outpointAndUTXOEntryPair.UTXOEntry.IsCoinbase(),
BlockBlueScore: outpointAndUTXOEntryPair.UTXOEntry.BlockBlueScore(),
},
}
}
return domainOutpointAndUTXOEntryPairs
}

View File

@@ -51,15 +51,18 @@ const (
CmdReject CmdReject
CmdHeader CmdHeader
CmdRequestNextHeaders CmdRequestNextHeaders
CmdRequestIBDRootUTXOSetAndBlock CmdRequestPruningPointUTXOSetAndBlock
CmdIBDRootUTXOSetAndBlock CmdPruningPointUTXOSetChunk
CmdRequestIBDBlocks CmdRequestIBDBlocks
CmdIBDRootNotFound CmdUnexpectedPruningPoint
CmdRequestIBDRootHash CmdRequestPruningPointHash
CmdIBDRootHash CmdPruningPointHash
CmdIBDBlockLocator CmdIBDBlockLocator
CmdIBDBlockLocatorHighestHash CmdIBDBlockLocatorHighestHash
CmdIBDBlockLocatorHighestHashNotFound
CmdBlockHeaders CmdBlockHeaders
CmdRequestNextPruningPointUTXOSetChunk
CmdDonePruningPointUTXOSetChunks
// rpc // rpc
CmdGetCurrentNetworkRequestMessage CmdGetCurrentNetworkRequestMessage
@@ -114,6 +117,8 @@ const (
CmdNotifyUTXOsChangedRequestMessage CmdNotifyUTXOsChangedRequestMessage
CmdNotifyUTXOsChangedResponseMessage CmdNotifyUTXOsChangedResponseMessage
CmdUTXOsChangedNotificationMessage CmdUTXOsChangedNotificationMessage
CmdStopNotifyingUTXOsChangedRequestMessage
CmdStopNotifyingUTXOsChangedResponseMessage
CmdGetUTXOsByAddressesRequestMessage CmdGetUTXOsByAddressesRequestMessage
CmdGetUTXOsByAddressesResponseMessage CmdGetUTXOsByAddressesResponseMessage
CmdGetVirtualSelectedParentBlueScoreRequestMessage CmdGetVirtualSelectedParentBlueScoreRequestMessage
@@ -121,40 +126,54 @@ const (
CmdNotifyVirtualSelectedParentBlueScoreChangedRequestMessage CmdNotifyVirtualSelectedParentBlueScoreChangedRequestMessage
CmdNotifyVirtualSelectedParentBlueScoreChangedResponseMessage CmdNotifyVirtualSelectedParentBlueScoreChangedResponseMessage
CmdVirtualSelectedParentBlueScoreChangedNotificationMessage CmdVirtualSelectedParentBlueScoreChangedNotificationMessage
CmdBanRequestMessage
CmdBanResponseMessage
CmdUnbanRequestMessage
CmdUnbanResponseMessage
CmdGetInfoRequestMessage
CmdGetInfoResponseMessage
CmdNotifyPruningPointUTXOSetOverrideRequestMessage
CmdNotifyPruningPointUTXOSetOverrideResponseMessage
CmdPruningPointUTXOSetOverrideNotificationMessage
CmdStopNotifyingPruningPointUTXOSetOverrideRequestMessage
CmdStopNotifyingPruningPointUTXOSetOverrideResponseMessage
) )
// ProtocolMessageCommandToString maps all MessageCommands to their string representation // ProtocolMessageCommandToString maps all MessageCommands to their string representation
var ProtocolMessageCommandToString = map[MessageCommand]string{ var ProtocolMessageCommandToString = map[MessageCommand]string{
CmdVersion: "Version", CmdVersion: "Version",
CmdVerAck: "VerAck", CmdVerAck: "VerAck",
CmdRequestAddresses: "RequestAddresses", CmdRequestAddresses: "RequestAddresses",
CmdAddresses: "Addresses", CmdAddresses: "Addresses",
CmdRequestHeaders: "RequestHeaders", CmdRequestHeaders: "RequestHeaders",
CmdBlock: "Block", CmdBlock: "Block",
CmdTx: "Tx", CmdTx: "Tx",
CmdPing: "Ping", CmdPing: "Ping",
CmdPong: "Pong", CmdPong: "Pong",
CmdRequestBlockLocator: "RequestBlockLocator", CmdRequestBlockLocator: "RequestBlockLocator",
CmdBlockLocator: "BlockLocator", CmdBlockLocator: "BlockLocator",
CmdInvRelayBlock: "InvRelayBlock", CmdInvRelayBlock: "InvRelayBlock",
CmdRequestRelayBlocks: "RequestRelayBlocks", CmdRequestRelayBlocks: "RequestRelayBlocks",
CmdInvTransaction: "InvTransaction", CmdInvTransaction: "InvTransaction",
CmdRequestTransactions: "RequestTransactions", CmdRequestTransactions: "RequestTransactions",
CmdIBDBlock: "IBDBlock", CmdIBDBlock: "IBDBlock",
CmdDoneHeaders: "DoneHeaders", CmdDoneHeaders: "DoneHeaders",
CmdTransactionNotFound: "TransactionNotFound", CmdTransactionNotFound: "TransactionNotFound",
CmdReject: "Reject", CmdReject: "Reject",
CmdHeader: "Header", CmdHeader: "Header",
CmdRequestNextHeaders: "RequestNextHeaders", CmdRequestNextHeaders: "RequestNextHeaders",
CmdRequestIBDRootUTXOSetAndBlock: "RequestPruningUTXOSetAndBlock", CmdRequestPruningPointUTXOSetAndBlock: "RequestPruningPointUTXOSetAndBlock",
CmdIBDRootUTXOSetAndBlock: "IBDRootUTXOSetAndBlock", CmdPruningPointUTXOSetChunk: "PruningPointUTXOSetChunk",
CmdRequestIBDBlocks: "RequestIBDBlocks", CmdRequestIBDBlocks: "RequestIBDBlocks",
CmdIBDRootNotFound: "IBDRootNotFound", CmdUnexpectedPruningPoint: "UnexpectedPruningPoint",
CmdRequestIBDRootHash: "IBDRequestIBDRootHash", CmdRequestPruningPointHash: "RequestPruningPointHashHash",
CmdIBDRootHash: "IBDIBDRootHash", CmdPruningPointHash: "PruningPointHash",
CmdIBDBlockLocator: "IBDBlockLocator", CmdIBDBlockLocator: "IBDBlockLocator",
CmdIBDBlockLocatorHighestHash: "IBDBlockLocatorHighestHash", CmdIBDBlockLocatorHighestHash: "IBDBlockLocatorHighestHash",
CmdBlockHeaders: "BlockHeaders", CmdIBDBlockLocatorHighestHashNotFound: "IBDBlockLocatorHighestHashNotFound",
CmdBlockHeaders: "BlockHeaders",
CmdRequestNextPruningPointUTXOSetChunk: "RequestNextPruningPointUTXOSetChunk",
CmdDonePruningPointUTXOSetChunks: "DonePruningPointUTXOSetChunks",
} }
// RPCMessageCommandToString maps all MessageCommands to their string representation // RPCMessageCommandToString maps all MessageCommands to their string representation
@@ -209,6 +228,8 @@ var RPCMessageCommandToString = map[MessageCommand]string{
CmdNotifyUTXOsChangedRequestMessage: "NotifyUTXOsChangedRequest", CmdNotifyUTXOsChangedRequestMessage: "NotifyUTXOsChangedRequest",
CmdNotifyUTXOsChangedResponseMessage: "NotifyUTXOsChangedResponse", CmdNotifyUTXOsChangedResponseMessage: "NotifyUTXOsChangedResponse",
CmdUTXOsChangedNotificationMessage: "UTXOsChangedNotification", CmdUTXOsChangedNotificationMessage: "UTXOsChangedNotification",
CmdStopNotifyingUTXOsChangedRequestMessage: "StopNotifyingUTXOsChangedRequest",
CmdStopNotifyingUTXOsChangedResponseMessage: "StopNotifyingUTXOsChangedResponse",
CmdGetUTXOsByAddressesRequestMessage: "GetUTXOsByAddressesRequest", CmdGetUTXOsByAddressesRequestMessage: "GetUTXOsByAddressesRequest",
CmdGetUTXOsByAddressesResponseMessage: "GetUTXOsByAddressesResponse", CmdGetUTXOsByAddressesResponseMessage: "GetUTXOsByAddressesResponse",
CmdGetVirtualSelectedParentBlueScoreRequestMessage: "GetVirtualSelectedParentBlueScoreRequest", CmdGetVirtualSelectedParentBlueScoreRequestMessage: "GetVirtualSelectedParentBlueScoreRequest",
@@ -216,6 +237,17 @@ var RPCMessageCommandToString = map[MessageCommand]string{
CmdNotifyVirtualSelectedParentBlueScoreChangedRequestMessage: "NotifyVirtualSelectedParentBlueScoreChangedRequest", CmdNotifyVirtualSelectedParentBlueScoreChangedRequestMessage: "NotifyVirtualSelectedParentBlueScoreChangedRequest",
CmdNotifyVirtualSelectedParentBlueScoreChangedResponseMessage: "NotifyVirtualSelectedParentBlueScoreChangedResponse", CmdNotifyVirtualSelectedParentBlueScoreChangedResponseMessage: "NotifyVirtualSelectedParentBlueScoreChangedResponse",
CmdVirtualSelectedParentBlueScoreChangedNotificationMessage: "VirtualSelectedParentBlueScoreChangedNotification", CmdVirtualSelectedParentBlueScoreChangedNotificationMessage: "VirtualSelectedParentBlueScoreChangedNotification",
CmdBanRequestMessage: "BanRequest",
CmdBanResponseMessage: "BanResponse",
CmdUnbanRequestMessage: "UnbanRequest",
CmdUnbanResponseMessage: "UnbanResponse",
CmdGetInfoRequestMessage: "GetInfoRequest",
CmdGetInfoResponseMessage: "GeInfoResponse",
CmdNotifyPruningPointUTXOSetOverrideRequestMessage: "NotifyPruningPointUTXOSetOverrideRequest",
CmdNotifyPruningPointUTXOSetOverrideResponseMessage: "NotifyPruningPointUTXOSetOverrideResponse",
CmdPruningPointUTXOSetOverrideNotificationMessage: "PruningPointUTXOSetOverrideNotification",
CmdStopNotifyingPruningPointUTXOSetOverrideRequestMessage: "StopNotifyingPruningPointUTXOSetOverrideRequest",
CmdStopNotifyingPruningPointUTXOSetOverrideResponseMessage: "StopNotifyingPruningPointUTXOSetOverrideResponse",
} }
// Message is an interface that describes a kaspa message. A type that // Message is an interface that describes a kaspa message. A type that

View File

@@ -1,22 +0,0 @@
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() *MsgIBDRootNotFound {
return &MsgIBDRootNotFound{}
}

View File

@@ -11,15 +11,11 @@ import (
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/util/mstime" "github.com/kaspanet/kaspad/util/mstime"
"github.com/kaspanet/kaspad/util/random"
) )
// TestBlockHeader tests the MsgBlockHeader API. // TestBlockHeader tests the MsgBlockHeader API.
func TestBlockHeader(t *testing.T) { func TestBlockHeader(t *testing.T) {
nonce, err := random.Uint64() nonce := uint64(0xba4d87a69924a93d)
if err != nil {
t.Errorf("random.Uint64: Error generating nonce: %v", err)
}
hashes := []*externalapi.DomainHash{mainnetGenesisHash, simnetGenesisHash} hashes := []*externalapi.DomainHash{mainnetGenesisHash, simnetGenesisHash}

View File

@@ -0,0 +1,16 @@
package appmessage
// MsgDonePruningPointUTXOSetChunks represents a kaspa DonePruningPointUTXOSetChunks message
type MsgDonePruningPointUTXOSetChunks struct {
baseMessage
}
// Command returns the protocol command string for the message
func (msg *MsgDonePruningPointUTXOSetChunks) Command() MessageCommand {
return CmdDonePruningPointUTXOSetChunks
}
// NewMsgDonePruningPointUTXOSetChunks returns a new MsgDonePruningPointUTXOSetChunks.
func NewMsgDonePruningPointUTXOSetChunks() *MsgDonePruningPointUTXOSetChunks {
return &MsgDonePruningPointUTXOSetChunks{}
}

View File

@@ -0,0 +1,16 @@
package appmessage
// MsgIBDBlockLocatorHighestHashNotFound represents a kaspa BlockLocatorHighestHashNotFound message
type MsgIBDBlockLocatorHighestHashNotFound struct {
baseMessage
}
// Command returns the protocol command string for the message
func (msg *MsgIBDBlockLocatorHighestHashNotFound) Command() MessageCommand {
return CmdIBDBlockLocatorHighestHashNotFound
}
// NewMsgIBDBlockLocatorHighestHashNotFound returns a new IBDBlockLocatorHighestHashNotFound message
func NewMsgIBDBlockLocatorHighestHashNotFound() *MsgIBDBlockLocatorHighestHashNotFound {
return &MsgIBDBlockLocatorHighestHashNotFound{}
}

View File

@@ -1,26 +0,0 @@
package appmessage
import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
)
// MsgIBDRootHashMessage implements the Message interface and represents a kaspa
// IBDRootHash message. It is used as a reply to IBD root hash requests.
type MsgIBDRootHashMessage struct {
baseMessage
Hash *externalapi.DomainHash
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgIBDRootHashMessage) Command() MessageCommand {
return CmdIBDRootHash
}
// NewMsgIBDRootHashMessage returns a new kaspa IBDRootHash message that conforms to
// the Message interface. See MsgIBDRootHashMessage for details.
func NewMsgIBDRootHashMessage(hash *externalapi.DomainHash) *MsgIBDRootHashMessage {
return &MsgIBDRootHashMessage{
Hash: hash,
}
}

View File

@@ -1,23 +0,0 @@
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

@@ -6,17 +6,12 @@ package appmessage
import ( import (
"testing" "testing"
"github.com/kaspanet/kaspad/util/random"
) )
// TestPing tests the MsgPing API against the latest protocol version. // TestPing tests the MsgPing API against the latest protocol version.
func TestPing(t *testing.T) { func TestPing(t *testing.T) {
// Ensure we get the same nonce back out. // Ensure we get the same nonce back out.
nonce, err := random.Uint64() nonce := uint64(0x61c2c5535902862)
if err != nil {
t.Errorf("random.Uint64: Error generating nonce: %v", err)
}
msg := NewMsgPing(nonce) msg := NewMsgPing(nonce)
if msg.Nonce != nonce { if msg.Nonce != nonce {
t.Errorf("NewMsgPing: wrong nonce - got %v, want %v", t.Errorf("NewMsgPing: wrong nonce - got %v, want %v",

View File

@@ -6,16 +6,11 @@ package appmessage
import ( import (
"testing" "testing"
"github.com/kaspanet/kaspad/util/random"
) )
// TestPongLatest tests the MsgPong API against the latest protocol version. // TestPongLatest tests the MsgPong API against the latest protocol version.
func TestPongLatest(t *testing.T) { func TestPongLatest(t *testing.T) {
nonce, err := random.Uint64() nonce := uint64(0x1a05b581a5182c)
if err != nil {
t.Errorf("random.Uint64: error generating nonce: %v", err)
}
msg := NewMsgPong(nonce) msg := NewMsgPong(nonce)
if msg.Nonce != nonce { if msg.Nonce != nonce {
t.Errorf("NewMsgPong: wrong nonce - got %v, want %v", t.Errorf("NewMsgPong: wrong nonce - got %v, want %v",

View File

@@ -0,0 +1,23 @@
package appmessage
import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
)
// MsgPruningPointHashMessage represents a kaspa PruningPointHash message
type MsgPruningPointHashMessage struct {
baseMessage
Hash *externalapi.DomainHash
}
// Command returns the protocol command string for the message
func (msg *MsgPruningPointHashMessage) Command() MessageCommand {
return CmdPruningPointHash
}
// NewPruningPointHashMessage returns a new kaspa PruningPointHash message
func NewPruningPointHashMessage(hash *externalapi.DomainHash) *MsgPruningPointHashMessage {
return &MsgPruningPointHashMessage{
Hash: hash,
}
}

View File

@@ -0,0 +1,36 @@
package appmessage
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
// MsgPruningPointUTXOSetChunk represents a kaspa PruningPointUTXOSetChunk message
type MsgPruningPointUTXOSetChunk struct {
baseMessage
OutpointAndUTXOEntryPairs []*OutpointAndUTXOEntryPair
}
// Command returns the protocol command string for the message
func (msg *MsgPruningPointUTXOSetChunk) Command() MessageCommand {
return CmdPruningPointUTXOSetChunk
}
// NewMsgPruningPointUTXOSetChunk returns a new MsgPruningPointUTXOSetChunk.
func NewMsgPruningPointUTXOSetChunk(outpointAndUTXOEntryPairs []*OutpointAndUTXOEntryPair) *MsgPruningPointUTXOSetChunk {
return &MsgPruningPointUTXOSetChunk{
OutpointAndUTXOEntryPairs: outpointAndUTXOEntryPairs,
}
}
// OutpointAndUTXOEntryPair is an outpoint along with its
// respective UTXO entry
type OutpointAndUTXOEntryPair struct {
Outpoint *Outpoint
UTXOEntry *UTXOEntry
}
// UTXOEntry houses details about an individual transaction output in a UTXO
type UTXOEntry struct {
Amount uint64
ScriptPublicKey *externalapi.ScriptPublicKey
BlockBlueScore uint64
IsCoinbase bool
}

View File

@@ -1,26 +0,0 @@
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,16 @@
package appmessage
// MsgRequestNextPruningPointUTXOSetChunk represents a kaspa RequestNextPruningPointUTXOSetChunk message
type MsgRequestNextPruningPointUTXOSetChunk struct {
baseMessage
}
// Command returns the protocol command string for the message
func (msg *MsgRequestNextPruningPointUTXOSetChunk) Command() MessageCommand {
return CmdRequestNextPruningPointUTXOSetChunk
}
// NewMsgRequestNextPruningPointUTXOSetChunk returns a new MsgRequestNextPruningPointUTXOSetChunk.
func NewMsgRequestNextPruningPointUTXOSetChunk() *MsgRequestNextPruningPointUTXOSetChunk {
return &MsgRequestNextPruningPointUTXOSetChunk{}
}

View File

@@ -0,0 +1,23 @@
package appmessage
import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
)
// MsgRequestPruningPointUTXOSetAndBlock represents a kaspa RequestPruningPointUTXOSetAndBlock message
type MsgRequestPruningPointUTXOSetAndBlock struct {
baseMessage
PruningPointHash *externalapi.DomainHash
}
// Command returns the protocol command string for the message
func (msg *MsgRequestPruningPointUTXOSetAndBlock) Command() MessageCommand {
return CmdRequestPruningPointUTXOSetAndBlock
}
// NewMsgRequestPruningPointUTXOSetAndBlock returns a new MsgRequestPruningPointUTXOSetAndBlock
func NewMsgRequestPruningPointUTXOSetAndBlock(pruningPointHash *externalapi.DomainHash) *MsgRequestPruningPointUTXOSetAndBlock {
return &MsgRequestPruningPointUTXOSetAndBlock{
PruningPointHash: pruningPointHash,
}
}

View File

@@ -1,22 +0,0 @@
package appmessage
// MsgRequestIBDRootHashMessage implements the Message interface and represents a kaspa
// MsgRequestIBDRootHashMessage message. It is used to request the IBD root hash
// from a peer during IBD.
//
// This message has no payload.
type MsgRequestIBDRootHashMessage struct {
baseMessage
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgRequestIBDRootHashMessage) Command() MessageCommand {
return CmdRequestIBDRootHash
}
// NewMsgRequestIBDRootHashMessage returns a new kaspa RequestIBDRootHash message that conforms to the
// Message interface.
func NewMsgRequestIBDRootHashMessage() *MsgRequestIBDRootHashMessage {
return &MsgRequestIBDRootHashMessage{}
}

View File

@@ -0,0 +1,16 @@
package appmessage
// MsgRequestPruningPointHashMessage represents a kaspa RequestPruningPointHashMessage message
type MsgRequestPruningPointHashMessage struct {
baseMessage
}
// Command returns the protocol command string for the message
func (msg *MsgRequestPruningPointHashMessage) Command() MessageCommand {
return CmdRequestPruningPointHash
}
// NewMsgRequestPruningPointHashMessage returns a new kaspa RequestPruningPointHash message
func NewMsgRequestPruningPointHashMessage() *MsgRequestPruningPointHashMessage {
return &MsgRequestPruningPointHashMessage{}
}

View File

@@ -0,0 +1,16 @@
package appmessage
// MsgUnexpectedPruningPoint represents a kaspa UnexpectedPruningPoint message
type MsgUnexpectedPruningPoint struct {
baseMessage
}
// Command returns the protocol command string for the message
func (msg *MsgUnexpectedPruningPoint) Command() MessageCommand {
return CmdUnexpectedPruningPoint
}
// NewMsgUnexpectedPruningPoint returns a new kaspa UnexpectedPruningPoint message
func NewMsgUnexpectedPruningPoint() *MsgUnexpectedPruningPoint {
return &MsgUnexpectedPruningPoint{}
}

39
app/appmessage/rpc_ban.go Normal file
View File

@@ -0,0 +1,39 @@
package appmessage
// BanRequestMessage is an appmessage corresponding to
// its respective RPC message
type BanRequestMessage struct {
baseMessage
IP string
}
// Command returns the protocol command string for the message
func (msg *BanRequestMessage) Command() MessageCommand {
return CmdBanRequestMessage
}
// NewBanRequestMessage returns an instance of the message
func NewBanRequestMessage(ip string) *BanRequestMessage {
return &BanRequestMessage{
IP: ip,
}
}
// BanResponseMessage is an appmessage corresponding to
// its respective RPC message
type BanResponseMessage struct {
baseMessage
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *BanResponseMessage) Command() MessageCommand {
return CmdBanResponseMessage
}
// NewBanResponseMessage returns a instance of the message
func NewBanResponseMessage() *BanResponseMessage {
return &BanResponseMessage{}
}

View File

@@ -5,7 +5,6 @@ package appmessage
type GetBlockRequestMessage struct { type GetBlockRequestMessage struct {
baseMessage baseMessage
Hash string Hash string
SubnetworkID string
IncludeTransactionVerboseData bool IncludeTransactionVerboseData bool
} }
@@ -15,10 +14,9 @@ func (msg *GetBlockRequestMessage) Command() MessageCommand {
} }
// NewGetBlockRequestMessage returns a instance of the message // NewGetBlockRequestMessage returns a instance of the message
func NewGetBlockRequestMessage(hash string, subnetworkID string, includeTransactionVerboseData bool) *GetBlockRequestMessage { func NewGetBlockRequestMessage(hash string, includeTransactionVerboseData bool) *GetBlockRequestMessage {
return &GetBlockRequestMessage{ return &GetBlockRequestMessage{
Hash: hash, Hash: hash,
SubnetworkID: subnetworkID,
IncludeTransactionVerboseData: includeTransactionVerboseData, IncludeTransactionVerboseData: includeTransactionVerboseData,
} }
} }
@@ -57,6 +55,7 @@ type BlockVerboseData struct {
Bits string Bits string
Difficulty float64 Difficulty float64
ParentHashes []string ParentHashes []string
ChildrenHashes []string
SelectedParentHash string SelectedParentHash string
BlueScore uint64 BlueScore uint64
IsHeaderOnly bool IsHeaderOnly bool
@@ -106,4 +105,5 @@ type ScriptPubKeyResult struct {
Hex string Hex string
Type string Type string
Address string Address string
Version uint16
} }

View File

@@ -27,6 +27,7 @@ type GetBlockDAGInfoResponseMessage struct {
VirtualParentHashes []string VirtualParentHashes []string
Difficulty float64 Difficulty float64
PastMedianTime int64 PastMedianTime int64
PruningPointHash string
Error *RPCError Error *RPCError
} }

View File

@@ -4,9 +4,9 @@ package appmessage
// its respective RPC message // its respective RPC message
type GetBlocksRequestMessage struct { type GetBlocksRequestMessage struct {
baseMessage baseMessage
LowHash string LowHash string
IncludeBlockHexes bool IncludeBlockVerboseData bool
IncludeBlockVerboseData bool IncludeTransactionVerboseData bool
} }
// Command returns the protocol command string for the message // Command returns the protocol command string for the message
@@ -15,11 +15,12 @@ func (msg *GetBlocksRequestMessage) Command() MessageCommand {
} }
// NewGetBlocksRequestMessage returns a instance of the message // NewGetBlocksRequestMessage returns a instance of the message
func NewGetBlocksRequestMessage(lowHash string, includeBlockHexes bool, includeBlockVerboseData bool) *GetBlocksRequestMessage { func NewGetBlocksRequestMessage(lowHash string, includeBlockVerboseData bool,
includeTransactionVerboseData bool) *GetBlocksRequestMessage {
return &GetBlocksRequestMessage{ return &GetBlocksRequestMessage{
LowHash: lowHash, LowHash: lowHash,
IncludeBlockHexes: includeBlockHexes, IncludeBlockVerboseData: includeBlockVerboseData,
IncludeBlockVerboseData: includeBlockVerboseData, IncludeTransactionVerboseData: includeTransactionVerboseData,
} }
} }
@@ -28,7 +29,6 @@ func NewGetBlocksRequestMessage(lowHash string, includeBlockHexes bool, includeB
type GetBlocksResponseMessage struct { type GetBlocksResponseMessage struct {
baseMessage baseMessage
BlockHashes []string BlockHashes []string
BlockHexes []string
BlockVerboseData []*BlockVerboseData BlockVerboseData []*BlockVerboseData
Error *RPCError Error *RPCError
@@ -45,7 +45,6 @@ func NewGetBlocksResponseMessage(blockHashes []string, blockHexes []string,
return &GetBlocksResponseMessage{ return &GetBlocksResponseMessage{
BlockHashes: blockHashes, BlockHashes: blockHashes,
BlockHexes: blockHexes,
BlockVerboseData: blockVerboseData, BlockVerboseData: blockVerboseData,
} }
} }

View File

@@ -46,4 +46,5 @@ type GetConnectedPeerInfoMessage struct {
UserAgent string UserAgent string
AdvertisedProtocolVersion uint32 AdvertisedProtocolVersion uint32
TimeConnected int64 TimeConnected int64
IsIBDPeer bool
} }

View File

@@ -0,0 +1,38 @@
package appmessage
// GetInfoRequestMessage is an appmessage corresponding to
// its respective RPC message
type GetInfoRequestMessage struct {
baseMessage
}
// Command returns the protocol command string for the message
func (msg *GetInfoRequestMessage) Command() MessageCommand {
return CmdGetInfoRequestMessage
}
// NewGeInfoRequestMessage returns a instance of the message
func NewGeInfoRequestMessage() *GetInfoRequestMessage {
return &GetInfoRequestMessage{}
}
// GetInfoResponseMessage is an appmessage corresponding to
// its respective RPC message
type GetInfoResponseMessage struct {
baseMessage
P2PID string
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *GetInfoResponseMessage) Command() MessageCommand {
return CmdGetInfoResponseMessage
}
// NewGetInfoResponseMessage returns a instance of the message
func NewGetInfoResponseMessage(p2pID string) *GetInfoResponseMessage {
return &GetInfoResponseMessage{
P2PID: p2pID,
}
}

View File

@@ -37,7 +37,8 @@ func NewNotifyBlockAddedResponseMessage() *NotifyBlockAddedResponseMessage {
// its respective RPC message // its respective RPC message
type BlockAddedNotificationMessage struct { type BlockAddedNotificationMessage struct {
baseMessage baseMessage
Block *MsgBlock Block *MsgBlock
BlockVerboseData *BlockVerboseData
} }
// Command returns the protocol command string for the message // Command returns the protocol command string for the message
@@ -46,8 +47,9 @@ func (msg *BlockAddedNotificationMessage) Command() MessageCommand {
} }
// NewBlockAddedNotificationMessage returns a instance of the message // NewBlockAddedNotificationMessage returns a instance of the message
func NewBlockAddedNotificationMessage(block *MsgBlock) *BlockAddedNotificationMessage { func NewBlockAddedNotificationMessage(block *MsgBlock, blockVerboseData *BlockVerboseData) *BlockAddedNotificationMessage {
return &BlockAddedNotificationMessage{ return &BlockAddedNotificationMessage{
Block: block, Block: block,
BlockVerboseData: blockVerboseData,
} }
} }

View File

@@ -0,0 +1,83 @@
package appmessage
// NotifyPruningPointUTXOSetOverrideRequestMessage is an appmessage corresponding to
// its respective RPC message
type NotifyPruningPointUTXOSetOverrideRequestMessage struct {
baseMessage
}
// Command returns the protocol command string for the message
func (msg *NotifyPruningPointUTXOSetOverrideRequestMessage) Command() MessageCommand {
return CmdNotifyPruningPointUTXOSetOverrideRequestMessage
}
// NewNotifyPruningPointUTXOSetOverrideRequestMessage returns a instance of the message
func NewNotifyPruningPointUTXOSetOverrideRequestMessage() *NotifyPruningPointUTXOSetOverrideRequestMessage {
return &NotifyPruningPointUTXOSetOverrideRequestMessage{}
}
// NotifyPruningPointUTXOSetOverrideResponseMessage is an appmessage corresponding to
// its respective RPC message
type NotifyPruningPointUTXOSetOverrideResponseMessage struct {
baseMessage
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *NotifyPruningPointUTXOSetOverrideResponseMessage) Command() MessageCommand {
return CmdNotifyPruningPointUTXOSetOverrideResponseMessage
}
// NewNotifyPruningPointUTXOSetOverrideResponseMessage returns a instance of the message
func NewNotifyPruningPointUTXOSetOverrideResponseMessage() *NotifyPruningPointUTXOSetOverrideResponseMessage {
return &NotifyPruningPointUTXOSetOverrideResponseMessage{}
}
// PruningPointUTXOSetOverrideNotificationMessage is an appmessage corresponding to
// its respective RPC message
type PruningPointUTXOSetOverrideNotificationMessage struct {
baseMessage
}
// Command returns the protocol command string for the message
func (msg *PruningPointUTXOSetOverrideNotificationMessage) Command() MessageCommand {
return CmdPruningPointUTXOSetOverrideNotificationMessage
}
// NewPruningPointUTXOSetOverrideNotificationMessage returns a instance of the message
func NewPruningPointUTXOSetOverrideNotificationMessage() *PruningPointUTXOSetOverrideNotificationMessage {
return &PruningPointUTXOSetOverrideNotificationMessage{}
}
// StopNotifyingPruningPointUTXOSetOverrideRequestMessage is an appmessage corresponding to
// its respective RPC message
type StopNotifyingPruningPointUTXOSetOverrideRequestMessage struct {
baseMessage
}
// Command returns the protocol command string for the message
func (msg *StopNotifyingPruningPointUTXOSetOverrideRequestMessage) Command() MessageCommand {
return CmdNotifyPruningPointUTXOSetOverrideRequestMessage
}
// NewStopNotifyingPruningPointUTXOSetOverrideRequestMessage returns a instance of the message
func NewStopNotifyingPruningPointUTXOSetOverrideRequestMessage() *StopNotifyingPruningPointUTXOSetOverrideRequestMessage {
return &StopNotifyingPruningPointUTXOSetOverrideRequestMessage{}
}
// StopNotifyingPruningPointUTXOSetOverrideResponseMessage is an appmessage corresponding to
// its respective RPC message
type StopNotifyingPruningPointUTXOSetOverrideResponseMessage struct {
baseMessage
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *StopNotifyingPruningPointUTXOSetOverrideResponseMessage) Command() MessageCommand {
return CmdNotifyPruningPointUTXOSetOverrideResponseMessage
}
// NewStopNotifyingPruningPointUTXOSetOverrideResponseMessage returns a instance of the message
func NewStopNotifyingPruningPointUTXOSetOverrideResponseMessage() *StopNotifyingPruningPointUTXOSetOverrideResponseMessage {
return &StopNotifyingPruningPointUTXOSetOverrideResponseMessage{}
}

View File

@@ -0,0 +1,37 @@
package appmessage
// StopNotifyingUTXOsChangedRequestMessage is an appmessage corresponding to
// its respective RPC message
type StopNotifyingUTXOsChangedRequestMessage struct {
baseMessage
Addresses []string
}
// Command returns the protocol command string for the message
func (msg *StopNotifyingUTXOsChangedRequestMessage) Command() MessageCommand {
return CmdStopNotifyingUTXOsChangedRequestMessage
}
// NewStopNotifyingUTXOsChangedRequestMessage returns a instance of the message
func NewStopNotifyingUTXOsChangedRequestMessage(addresses []string) *StopNotifyingUTXOsChangedRequestMessage {
return &StopNotifyingUTXOsChangedRequestMessage{
Addresses: addresses,
}
}
// StopNotifyingUTXOsChangedResponseMessage is an appmessage corresponding to
// its respective RPC message
type StopNotifyingUTXOsChangedResponseMessage struct {
baseMessage
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *StopNotifyingUTXOsChangedResponseMessage) Command() MessageCommand {
return CmdStopNotifyingUTXOsChangedResponseMessage
}
// NewStopNotifyingUTXOsChangedResponseMessage returns a instance of the message
func NewStopNotifyingUTXOsChangedResponseMessage() *StopNotifyingUTXOsChangedResponseMessage {
return &StopNotifyingUTXOsChangedResponseMessage{}
}

View File

@@ -19,11 +19,33 @@ func NewSubmitBlockRequestMessage(block *MsgBlock) *SubmitBlockRequestMessage {
} }
} }
// RejectReason describes the reason why a block sent by SubmitBlock was rejected
type RejectReason byte
// RejectReason constants
// Not using iota, since in the .proto file those are hardcoded
const (
RejectReasonNone RejectReason = 0
RejectReasonBlockInvalid RejectReason = 1
RejectReasonIsInIBD RejectReason = 2
)
var rejectReasonToString = map[RejectReason]string{
RejectReasonNone: "None",
RejectReasonBlockInvalid: "Block is invalid",
RejectReasonIsInIBD: "Node is in IBD",
}
func (rr RejectReason) String() string {
return rejectReasonToString[rr]
}
// SubmitBlockResponseMessage is an appmessage corresponding to // SubmitBlockResponseMessage is an appmessage corresponding to
// its respective RPC message // its respective RPC message
type SubmitBlockResponseMessage struct { type SubmitBlockResponseMessage struct {
baseMessage baseMessage
Error *RPCError RejectReason RejectReason
Error *RPCError
} }
// Command returns the protocol command string for the message // Command returns the protocol command string for the message

View File

@@ -0,0 +1,39 @@
package appmessage
// UnbanRequestMessage is an appmessage corresponding to
// its respective RPC message
type UnbanRequestMessage struct {
baseMessage
IP string
}
// Command returns the protocol command string for the message
func (msg *UnbanRequestMessage) Command() MessageCommand {
return CmdUnbanRequestMessage
}
// NewUnbanRequestMessage returns an instance of the message
func NewUnbanRequestMessage(ip string) *UnbanRequestMessage {
return &UnbanRequestMessage{
IP: ip,
}
}
// UnbanResponseMessage is an appmessage corresponding to
// its respective RPC message
type UnbanResponseMessage struct {
baseMessage
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *UnbanResponseMessage) Command() MessageCommand {
return CmdUnbanResponseMessage
}
// NewUnbanResponseMessage returns a instance of the message
func NewUnbanResponseMessage() *UnbanResponseMessage {
return &UnbanResponseMessage{}
}

View File

@@ -2,9 +2,10 @@ package app
import ( import (
"fmt" "fmt"
"github.com/kaspanet/kaspad/domain/utxoindex"
"sync/atomic" "sync/atomic"
"github.com/kaspanet/kaspad/domain/utxoindex"
infrastructuredatabase "github.com/kaspanet/kaspad/infrastructure/db/database" infrastructuredatabase "github.com/kaspanet/kaspad/infrastructure/db/database"
"github.com/kaspanet/kaspad/domain" "github.com/kaspanet/kaspad/domain"
@@ -79,7 +80,7 @@ func (a *ComponentManager) Stop() {
func NewComponentManager(cfg *config.Config, db infrastructuredatabase.Database, interrupt chan<- struct{}) ( func NewComponentManager(cfg *config.Config, db infrastructuredatabase.Database, interrupt chan<- struct{}) (
*ComponentManager, error) { *ComponentManager, error) {
domain, err := domain.New(cfg.ActiveNetParams, db) domain, err := domain.New(cfg.ActiveNetParams, db, cfg.IsArchivalNode)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -89,14 +90,18 @@ func NewComponentManager(cfg *config.Config, db infrastructuredatabase.Database,
return nil, err return nil, err
} }
addressManager, err := addressmanager.New(addressmanager.NewConfig(cfg)) addressManager, err := addressmanager.New(addressmanager.NewConfig(cfg), db)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var utxoIndex *utxoindex.UTXOIndex var utxoIndex *utxoindex.UTXOIndex
if cfg.UTXOIndex { if cfg.UTXOIndex {
utxoIndex = utxoindex.New(domain.Consensus(), db) utxoIndex, err = utxoindex.New(domain.Consensus(), db)
if err != nil {
return nil, err
}
log.Infof("UTXO index started") log.Infof("UTXO index started")
} }
@@ -143,6 +148,7 @@ func setupRPC(
shutDownChan, shutDownChan,
) )
protocolManager.SetOnBlockAddedToDAGHandler(rpcManager.NotifyBlockAddedToDAG) protocolManager.SetOnBlockAddedToDAGHandler(rpcManager.NotifyBlockAddedToDAG)
protocolManager.SetOnPruningPointUTXOSetOverrideHandler(rpcManager.NotifyPruningPointUTXOSetOverride)
return rpcManager return rpcManager
} }

View File

@@ -7,8 +7,6 @@ package app
import ( import (
"github.com/kaspanet/kaspad/infrastructure/logger" "github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/util/panics"
) )
var log, _ = logger.Get(logger.SubsystemTags.KASD) var log = logger.RegisterSubSystem("KASD")
var spawn = panics.GoroutineWrapperFunc(log)

View File

@@ -1,58 +0,0 @@
// Copyright (c) 2015-2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blocklogger
import (
"sync"
"time"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/util/mstime"
)
var (
receivedLogBlocks int64
receivedLogTx int64
lastBlockLogTime = mstime.Now()
mtx sync.Mutex
)
// 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) {
mtx.Lock()
defer mtx.Unlock()
receivedLogBlocks++
receivedLogTx += int64(len(block.Transactions))
now := mstime.Now()
duration := now.Sub(lastBlockLogTime)
if duration < time.Second*10 {
return
}
// Truncate the duration to 10s of milliseconds.
tDuration := duration.Round(10 * time.Millisecond)
// Log information about new block blue score.
blockStr := "blocks"
if receivedLogBlocks == 1 {
blockStr = "block"
}
txStr := "transactions"
if receivedLogTx == 1 {
txStr = "transaction"
}
log.Infof("Processed %d %s in the last %s (%d %s, %s)",
receivedLogBlocks, blockStr, tDuration, receivedLogTx,
txStr, mstime.UnixMilliseconds(block.Header.TimeInMilliseconds()))
receivedLogBlocks = 0
receivedLogTx = 0
lastBlockLogTime = now
}

View File

@@ -1,9 +1,8 @@
package flowcontext package flowcontext
import ( import (
"sync/atomic" peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
"github.com/kaspanet/kaspad/app/protocol/blocklogger"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors" "github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/pkg/errors" "github.com/pkg/errors"
@@ -39,10 +38,11 @@ func (f *FlowContext) OnNewBlock(block *externalapi.DomainBlock,
} }
for i, newBlock := range newBlocks { for i, newBlock := range newBlocks {
blocklogger.LogBlock(block)
log.Debugf("OnNewBlock: passing block %s transactions to mining manager", hash) log.Debugf("OnNewBlock: passing block %s transactions to mining manager", hash)
_ = f.Domain().MiningManager().HandleNewBlockTransactions(newBlock.Transactions) _, err = f.Domain().MiningManager().HandleNewBlockTransactions(newBlock.Transactions)
if err != nil {
return err
}
if f.onBlockAddedToDAGHandler != nil { if f.onBlockAddedToDAGHandler != nil {
log.Debugf("OnNewBlock: calling f.onBlockAddedToDAGHandler for block %s", hash) log.Debugf("OnNewBlock: calling f.onBlockAddedToDAGHandler for block %s", hash)
@@ -57,6 +57,15 @@ func (f *FlowContext) OnNewBlock(block *externalapi.DomainBlock,
return nil return nil
} }
// OnPruningPointUTXOSetOverride calls the handler function whenever the UTXO set
// resets due to pruning point change via IBD.
func (f *FlowContext) OnPruningPointUTXOSetOverride() error {
if f.onPruningPointUTXOSetOverrideHandler != nil {
return f.onPruningPointUTXOSetOverrideHandler()
}
return nil
}
func (f *FlowContext) broadcastTransactionsAfterBlockAdded( func (f *FlowContext) broadcastTransactionsAfterBlockAdded(
block *externalapi.DomainBlock, transactionsAcceptedToMempool []*externalapi.DomainTransaction) error { block *externalapi.DomainBlock, transactionsAcceptedToMempool []*externalapi.DomainTransaction) error {
@@ -99,6 +108,10 @@ func (f *FlowContext) SharedRequestedBlocks() *blockrelay.SharedRequestedBlocks
// AddBlock adds the given block to the DAG and propagates it. // AddBlock adds the given block to the DAG and propagates it.
func (f *FlowContext) AddBlock(block *externalapi.DomainBlock) error { func (f *FlowContext) AddBlock(block *externalapi.DomainBlock) error {
if len(block.Transactions) == 0 {
return protocolerrors.Errorf(false, "cannot add header only block")
}
blockInsertionResult, err := f.Domain().Consensus().ValidateAndInsertBlock(block) blockInsertionResult, err := f.Domain().Consensus().ValidateAndInsertBlock(block)
if err != nil { if err != nil {
if errors.As(err, &ruleerrors.RuleError{}) { if errors.As(err, &ruleerrors.RuleError{}) {
@@ -115,24 +128,45 @@ func (f *FlowContext) AddBlock(block *externalapi.DomainBlock) error {
// IsIBDRunning returns true if IBD is currently marked as running // IsIBDRunning returns true if IBD is currently marked as running
func (f *FlowContext) IsIBDRunning() bool { func (f *FlowContext) IsIBDRunning() bool {
return atomic.LoadUint32(&f.isInIBD) != 0 f.ibdPeerMutex.RLock()
defer f.ibdPeerMutex.RUnlock()
return f.ibdPeer != nil
} }
// TrySetIBDRunning attempts to set `isInIBD`. Returns false // TrySetIBDRunning attempts to set `isInIBD`. Returns false
// if it is already set // if it is already set
func (f *FlowContext) TrySetIBDRunning() bool { func (f *FlowContext) TrySetIBDRunning(ibdPeer *peerpkg.Peer) bool {
succeeded := atomic.CompareAndSwapUint32(&f.isInIBD, 0, 1) f.ibdPeerMutex.Lock()
if succeeded { defer f.ibdPeerMutex.Unlock()
log.Infof("IBD started")
if f.ibdPeer != nil {
return false
} }
return succeeded f.ibdPeer = ibdPeer
log.Infof("IBD started")
return true
} }
// UnsetIBDRunning unsets isInIBD // UnsetIBDRunning unsets isInIBD
func (f *FlowContext) UnsetIBDRunning() { func (f *FlowContext) UnsetIBDRunning() {
succeeded := atomic.CompareAndSwapUint32(&f.isInIBD, 1, 0) f.ibdPeerMutex.Lock()
if !succeeded { defer f.ibdPeerMutex.Unlock()
if f.ibdPeer == nil {
panic("attempted to unset isInIBD when it was not set to begin with") panic("attempted to unset isInIBD when it was not set to begin with")
} }
f.ibdPeer = nil
log.Infof("IBD finished") log.Infof("IBD finished")
} }
// IBDPeer returns the current IBD peer or null if the node is not
// in IBD
func (f *FlowContext) IBDPeer() *peerpkg.Peer {
f.ibdPeerMutex.RLock()
defer f.ibdPeerMutex.RUnlock()
return f.ibdPeer
}

View File

@@ -18,11 +18,11 @@ import (
func (*FlowContext) HandleError(err error, flowName string, isStopping *uint32, errChan chan<- error) { func (*FlowContext) HandleError(err error, flowName string, isStopping *uint32, errChan chan<- error) {
isErrRouteClosed := errors.Is(err, router.ErrRouteClosed) isErrRouteClosed := errors.Is(err, router.ErrRouteClosed)
if !isErrRouteClosed { if !isErrRouteClosed {
if protocolErr := &(protocolerrors.ProtocolError{}); !errors.As(err, &protocolErr) { if protocolErr := (protocolerrors.ProtocolError{}); !errors.As(err, &protocolErr) {
panic(err) panic(err)
} }
log.Errorf("error from %s: %+v", flowName, err) log.Errorf("error from %s: %s", flowName, err)
} }
if atomic.AddUint32(isStopping, 1) == 1 { if atomic.AddUint32(isStopping, 1) == 1 {

View File

@@ -23,6 +23,10 @@ import (
// when a block is added to the DAG // when a block is added to the DAG
type OnBlockAddedToDAGHandler func(block *externalapi.DomainBlock, blockInsertionResult *externalapi.BlockInsertionResult) error type OnBlockAddedToDAGHandler func(block *externalapi.DomainBlock, blockInsertionResult *externalapi.BlockInsertionResult) error
// OnPruningPointUTXOSetOverrideHandler is a handle function that's triggered whenever the UTXO set
// resets due to pruning point change via IBD.
type OnPruningPointUTXOSetOverrideHandler func() error
// OnTransactionAddedToMempoolHandler is a handler function that's triggered // OnTransactionAddedToMempoolHandler is a handler function that's triggered
// when a transaction is added to the mempool // when a transaction is added to the mempool
type OnTransactionAddedToMempoolHandler func() type OnTransactionAddedToMempoolHandler func()
@@ -38,8 +42,9 @@ type FlowContext struct {
timeStarted int64 timeStarted int64
onBlockAddedToDAGHandler OnBlockAddedToDAGHandler onBlockAddedToDAGHandler OnBlockAddedToDAGHandler
onTransactionAddedToMempoolHandler OnTransactionAddedToMempoolHandler onPruningPointUTXOSetOverrideHandler OnPruningPointUTXOSetOverrideHandler
onTransactionAddedToMempoolHandler OnTransactionAddedToMempoolHandler
transactionsToRebroadcastLock sync.Mutex transactionsToRebroadcastLock sync.Mutex
transactionsToRebroadcast map[externalapi.DomainTransactionID]*externalapi.DomainTransaction transactionsToRebroadcast map[externalapi.DomainTransactionID]*externalapi.DomainTransaction
@@ -48,7 +53,8 @@ type FlowContext struct {
sharedRequestedBlocks *blockrelay.SharedRequestedBlocks sharedRequestedBlocks *blockrelay.SharedRequestedBlocks
isInIBD uint32 ibdPeer *peerpkg.Peer
ibdPeerMutex sync.RWMutex
peers map[id.ID]*peerpkg.Peer peers map[id.ID]*peerpkg.Peer
peersMutex sync.RWMutex peersMutex sync.RWMutex
@@ -81,6 +87,11 @@ func (f *FlowContext) SetOnBlockAddedToDAGHandler(onBlockAddedToDAGHandler OnBlo
f.onBlockAddedToDAGHandler = onBlockAddedToDAGHandler f.onBlockAddedToDAGHandler = onBlockAddedToDAGHandler
} }
// SetOnPruningPointUTXOSetOverrideHandler sets the onPruningPointUTXOSetOverrideHandler handler
func (f *FlowContext) SetOnPruningPointUTXOSetOverrideHandler(onPruningPointUTXOSetOverrideHandler OnPruningPointUTXOSetOverrideHandler) {
f.onPruningPointUTXOSetOverrideHandler = onPruningPointUTXOSetOverrideHandler
}
// SetOnTransactionAddedToMempoolHandler sets the onTransactionAddedToMempool handler // SetOnTransactionAddedToMempoolHandler sets the onTransactionAddedToMempool handler
func (f *FlowContext) SetOnTransactionAddedToMempoolHandler(onTransactionAddedToMempoolHandler OnTransactionAddedToMempoolHandler) { func (f *FlowContext) SetOnTransactionAddedToMempoolHandler(onTransactionAddedToMempoolHandler OnTransactionAddedToMempoolHandler) {
f.onTransactionAddedToMempoolHandler = onTransactionAddedToMempoolHandler f.onTransactionAddedToMempoolHandler = onTransactionAddedToMempoolHandler

View File

@@ -2,8 +2,6 @@ package flowcontext
import ( import (
"github.com/kaspanet/kaspad/infrastructure/logger" "github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/util/panics"
) )
var log, _ = logger.Get(logger.SubsystemTags.PROT) var log = logger.RegisterSubSystem("PROT")
var spawn = panics.GoroutineWrapperFunc(log)

View File

@@ -4,6 +4,8 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors" "github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing" "github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/domain/consensus/utils/hashset"
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@@ -160,3 +162,52 @@ func (f *FlowContext) unorphanBlock(orphanHash externalapi.DomainHash) (*externa
log.Infof("Unorphaned block %s", orphanHash) log.Infof("Unorphaned block %s", orphanHash)
return blockInsertionResult, true, nil return blockInsertionResult, true, nil
} }
// GetOrphanRoots returns the roots of the missing ancestors DAG of the given orphan
func (f *FlowContext) GetOrphanRoots(orphan *externalapi.DomainHash) ([]*externalapi.DomainHash, bool, error) {
onEnd := logger.LogAndMeasureExecutionTime(log, "GetOrphanRoots")
defer onEnd()
f.orphansMutex.RLock()
defer f.orphansMutex.RUnlock()
_, ok := f.orphans[*orphan]
if !ok {
return nil, false, nil
}
queue := []*externalapi.DomainHash{orphan}
addedToQueueSet := hashset.New()
addedToQueueSet.Add(orphan)
roots := []*externalapi.DomainHash{}
for len(queue) > 0 {
var current *externalapi.DomainHash
current, queue = queue[0], queue[1:]
block, ok := f.orphans[*current]
if !ok {
blockInfo, err := f.domain.Consensus().GetBlockInfo(current)
if err != nil {
return nil, false, err
}
if !blockInfo.Exists || blockInfo.BlockStatus == externalapi.StatusHeaderOnly {
roots = append(roots, current)
} else {
log.Debugf("Block %s was skipped when checking for orphan roots: "+
"exists: %t, status: %s", current, blockInfo.Exists, blockInfo.BlockStatus)
}
continue
}
for _, parent := range block.Header.ParentHashes() {
if !addedToQueueSet.Contains(parent) {
queue = append(queue, parent)
addedToQueueSet.Add(parent)
}
}
}
return roots, true, nil
}

View File

@@ -3,7 +3,7 @@ package flowcontext
import "github.com/kaspanet/kaspad/util/mstime" import "github.com/kaspanet/kaspad/util/mstime"
const ( const (
maxSelectedParentTimeDiffToAllowMiningInMilliSeconds = 300_000 maxSelectedParentTimeDiffToAllowMiningInMilliSeconds = 60 * 60 * 1000 // 1 Hour
) )
// ShouldMine returns whether it's ok to use block template from this node // ShouldMine returns whether it's ok to use block template from this node

View File

@@ -5,14 +5,12 @@ import (
"github.com/kaspanet/kaspad/app/protocol/common" "github.com/kaspanet/kaspad/app/protocol/common"
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer" peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
"github.com/kaspanet/kaspad/app/protocol/protocolerrors" "github.com/kaspanet/kaspad/app/protocol/protocolerrors"
"github.com/kaspanet/kaspad/infrastructure/config"
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager" "github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
) )
// ReceiveAddressesContext is the interface for the context needed for the ReceiveAddresses flow. // ReceiveAddressesContext is the interface for the context needed for the ReceiveAddresses flow.
type ReceiveAddressesContext interface { type ReceiveAddressesContext interface {
Config() *config.Config
AddressManager() *addressmanager.AddressManager AddressManager() *addressmanager.AddressManager
} }
@@ -37,6 +35,5 @@ func ReceiveAddresses(context ReceiveAddressesContext, incomingRoute *router.Rou
return protocolerrors.Errorf(true, "address count exceeded %d", addressmanager.GetAddressesMax) return protocolerrors.Errorf(true, "address count exceeded %d", addressmanager.GetAddressesMax)
} }
context.AddressManager().AddAddresses(msgAddresses.AddressList...) return context.AddressManager().AddAddresses(msgAddresses.AddressList...)
return nil
} }

View File

@@ -1,7 +1,6 @@
package addressexchange package addressexchange
import ( import (
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
"math/rand" "math/rand"
"github.com/kaspanet/kaspad/app/appmessage" "github.com/kaspanet/kaspad/app/appmessage"
@@ -17,16 +16,11 @@ type SendAddressesContext interface {
// SendAddresses sends addresses to a peer that requests it. // SendAddresses sends addresses to a peer that requests it.
func SendAddresses(context SendAddressesContext, incomingRoute *router.Route, outgoingRoute *router.Route) error { func SendAddresses(context SendAddressesContext, incomingRoute *router.Route, outgoingRoute *router.Route) error {
for { for {
message, err := incomingRoute.Dequeue() _, err := incomingRoute.Dequeue()
if err != nil { if err != nil {
return err 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() addresses := context.AddressManager().Addresses()
msgAddresses := appmessage.NewMsgAddresses(shuffleAddresses(addresses)) msgAddresses := appmessage.NewMsgAddresses(shuffleAddresses(addresses))

View File

@@ -70,8 +70,14 @@ func HandleIBDBlockLocator(context HandleIBDBlockLocatorContext, incomingRoute *
} }
if !foundHighestHashInTheSelectedParentChainOfTargetHash { if !foundHighestHashInTheSelectedParentChainOfTargetHash {
return protocolerrors.Errorf(true, "no hash was found in the blockLocator "+ log.Warnf("no hash was found in the blockLocator "+
"that was in the selected parent chain of targetHash %s", targetHash) "that was in the selected parent chain of targetHash %s", targetHash)
ibdBlockLocatorHighestHashNotFoundMessage := appmessage.NewMsgIBDBlockLocatorHighestHashNotFound()
err = outgoingRoute.Enqueue(ibdBlockLocatorHighestHashNotFoundMessage)
if err != nil {
return err
}
} }
} }
} }

View File

@@ -25,7 +25,8 @@ func HandleIBDBlockRequests(context HandleIBDBlockRequestsContext, incomingRoute
return err return err
} }
msgRequestIBDBlocks := message.(*appmessage.MsgRequestIBDBlocks) msgRequestIBDBlocks := message.(*appmessage.MsgRequestIBDBlocks)
for _, hash := range msgRequestIBDBlocks.Hashes { log.Debugf("Got request for %d ibd blocks", len(msgRequestIBDBlocks.Hashes))
for i, hash := range msgRequestIBDBlocks.Hashes {
// Fetch the block from the database. // Fetch the block from the database.
blockInfo, err := context.Domain().Consensus().GetBlockInfo(hash) blockInfo, err := context.Domain().Consensus().GetBlockInfo(hash)
if err != nil { if err != nil {
@@ -47,6 +48,7 @@ func HandleIBDBlockRequests(context HandleIBDBlockRequestsContext, incomingRoute
if err != nil { if err != nil {
return err return err
} }
log.Debugf("sent %d out of %d", i, len(msgRequestIBDBlocks.Hashes))
} }
} }
} }

View File

@@ -1,49 +0,0 @@
package blockrelay
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/domain"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
)
// HandleIBDRootHashRequestsFlowContext is the interface for the context needed for the handleIBDRootHashRequestsFlow flow.
type HandleIBDRootHashRequestsFlowContext interface {
Domain() domain.Domain
}
type handleIBDRootHashRequestsFlow struct {
HandleIBDRootHashRequestsFlowContext
incomingRoute, outgoingRoute *router.Route
}
// HandleIBDRootHashRequests listens to appmessage.MsgRequestIBDRootHashMessage messages and sends
// the IBD root hash as response.
func HandleIBDRootHashRequests(context HandleIBDRootHashRequestsFlowContext, incomingRoute,
outgoingRoute *router.Route) error {
flow := &handleIBDRootHashRequestsFlow{
HandleIBDRootHashRequestsFlowContext: context,
incomingRoute: incomingRoute,
outgoingRoute: outgoingRoute,
}
return flow.start()
}
func (flow *handleIBDRootHashRequestsFlow) start() error {
for {
_, err := flow.incomingRoute.Dequeue()
if err != nil {
return err
}
pruningPoint, err := flow.Domain().Consensus().PruningPoint()
if err != nil {
return err
}
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgIBDRootHashMessage(pruningPoint))
if err != nil {
return err
}
}
}

View File

@@ -0,0 +1,51 @@
package blockrelay
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/domain"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
)
// HandlePruningPointHashRequestsFlowContext is the interface for the context needed for the handlePruningPointHashRequestsFlow flow.
type HandlePruningPointHashRequestsFlowContext interface {
Domain() domain.Domain
}
type handlePruningPointHashRequestsFlow struct {
HandlePruningPointHashRequestsFlowContext
incomingRoute, outgoingRoute *router.Route
}
// HandlePruningPointHashRequests listens to appmessage.MsgRequestPruningPointHashMessage messages and sends
// the pruning point hash as response.
func HandlePruningPointHashRequests(context HandlePruningPointHashRequestsFlowContext, incomingRoute,
outgoingRoute *router.Route) error {
flow := &handlePruningPointHashRequestsFlow{
HandlePruningPointHashRequestsFlowContext: context,
incomingRoute: incomingRoute,
outgoingRoute: outgoingRoute,
}
return flow.start()
}
func (flow *handlePruningPointHashRequestsFlow) start() error {
for {
_, err := flow.incomingRoute.Dequeue()
if err != nil {
return err
}
log.Debugf("Got request for a pruning point hash")
pruningPoint, err := flow.Domain().Consensus().PruningPoint()
if err != nil {
return err
}
err = flow.outgoingRoute.Enqueue(appmessage.NewPruningPointHashMessage(pruningPoint))
if err != nil {
return err
}
log.Debugf("Sent pruning point hash %s", pruningPoint)
}
}

View File

@@ -26,6 +26,7 @@ func HandleRelayBlockRequests(context RelayBlockRequestsContext, incomingRoute *
return err return err
} }
getRelayBlocksMessage := message.(*appmessage.MsgRequestRelayBlocks) getRelayBlocksMessage := message.(*appmessage.MsgRequestRelayBlocks)
log.Debugf("Got request for relay blocks with hashes %s", getRelayBlocksMessage.Hashes)
for _, hash := range getRelayBlocksMessage.Hashes { for _, hash := range getRelayBlocksMessage.Hashes {
// Fetch the block from the database. // Fetch the block from the database.
blockInfo, err := context.Domain().Consensus().GetBlockInfo(hash) blockInfo, err := context.Domain().Consensus().GetBlockInfo(hash)
@@ -46,6 +47,7 @@ func HandleRelayBlockRequests(context RelayBlockRequestsContext, incomingRoute *
if err != nil { if err != nil {
return err return err
} }
log.Debugf("Relayed block with hash %s", hash)
} }
} }
} }

View File

@@ -10,7 +10,6 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors" "github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing" "github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/infrastructure/config" "github.com/kaspanet/kaspad/infrastructure/config"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@@ -24,14 +23,15 @@ var orphanResolutionRange uint32 = 5
type RelayInvsContext interface { type RelayInvsContext interface {
Domain() domain.Domain Domain() domain.Domain
Config() *config.Config Config() *config.Config
NetAdapter() *netadapter.NetAdapter
OnNewBlock(block *externalapi.DomainBlock, blockInsertionResult *externalapi.BlockInsertionResult) error OnNewBlock(block *externalapi.DomainBlock, blockInsertionResult *externalapi.BlockInsertionResult) error
OnPruningPointUTXOSetOverride() error
SharedRequestedBlocks() *SharedRequestedBlocks SharedRequestedBlocks() *SharedRequestedBlocks
Broadcast(message appmessage.Message) error Broadcast(message appmessage.Message) error
AddOrphan(orphanBlock *externalapi.DomainBlock) AddOrphan(orphanBlock *externalapi.DomainBlock)
GetOrphanRoots(orphanHash *externalapi.DomainHash) ([]*externalapi.DomainHash, bool, error)
IsOrphan(blockHash *externalapi.DomainHash) bool IsOrphan(blockHash *externalapi.DomainHash) bool
IsIBDRunning() bool IsIBDRunning() bool
TrySetIBDRunning() bool TrySetIBDRunning(ibdPeer *peerpkg.Peer) bool
UnsetIBDRunning() UnsetIBDRunning()
} }
@@ -71,7 +71,7 @@ func (flow *handleRelayInvsFlow) start() error {
if err != nil { if err != nil {
return err return err
} }
if blockInfo.Exists { if blockInfo.Exists && blockInfo.BlockStatus != externalapi.StatusHeaderOnly {
if blockInfo.BlockStatus == externalapi.StatusInvalid { if blockInfo.BlockStatus == externalapi.StatusInvalid {
return protocolerrors.Errorf(true, "sent inv of an invalid block %s", return protocolerrors.Errorf(true, "sent inv of an invalid block %s",
inv.Hash) inv.Hash)
@@ -81,7 +81,11 @@ func (flow *handleRelayInvsFlow) start() error {
} }
if flow.IsOrphan(inv.Hash) { if flow.IsOrphan(inv.Hash) {
log.Debugf("Block %s is a known orphan. continuing...", inv.Hash) log.Debugf("Block %s is a known orphan. Requesting its missing ancestors", inv.Hash)
err := flow.AddOrphanRootsToQueue(inv.Hash)
if err != nil {
return err
}
continue continue
} }
@@ -101,13 +105,27 @@ func (flow *handleRelayInvsFlow) start() error {
continue continue
} }
log.Debugf("Processing block %s", inv.Hash) err = flow.banIfBlockIsHeaderOnly(block)
missingParents, blockInsertionResult, err := flow.processBlock(block)
if err != nil { if err != nil {
return err return err
} }
log.Debugf("Processing block %s", inv.Hash)
missingParents, blockInsertionResult, err := flow.processBlock(block)
if err != nil {
if errors.Is(err, ruleerrors.ErrPrunedBlock) {
log.Infof("Ignoring pruned block %s", inv.Hash)
continue
}
if errors.Is(err, ruleerrors.ErrDuplicateBlock) {
log.Infof("Ignoring duplicate block %s", inv.Hash)
continue
}
return err
}
if len(missingParents) > 0 { if len(missingParents) > 0 {
log.Debugf("Block %s contains orphans: %s", inv.Hash, missingParents) log.Debugf("Block %s is orphan and has missing parents: %s", inv.Hash, missingParents)
err := flow.processOrphan(block, missingParents) err := flow.processOrphan(block, missingParents)
if err != nil { if err != nil {
return err return err
@@ -128,6 +146,15 @@ func (flow *handleRelayInvsFlow) start() error {
} }
} }
func (flow *handleRelayInvsFlow) banIfBlockIsHeaderOnly(block *externalapi.DomainBlock) error {
if len(block.Transactions) == 0 {
return protocolerrors.Errorf(true, "sent header of %s block where expected block with body",
consensushashing.BlockHash(block))
}
return nil
}
func (flow *handleRelayInvsFlow) readInv() (*appmessage.MsgInvRelayBlock, error) { func (flow *handleRelayInvsFlow) readInv() (*appmessage.MsgInvRelayBlock, error) {
if len(flow.invsQueue) > 0 { if len(flow.invsQueue) > 0 {
var inv *appmessage.MsgInvRelayBlock var inv *appmessage.MsgInvRelayBlock
@@ -238,9 +265,10 @@ func (flow *handleRelayInvsFlow) processOrphan(block *externalapi.DomainBlock, m
} }
if isBlockInOrphanResolutionRange { if isBlockInOrphanResolutionRange {
log.Debugf("Block %s is within orphan resolution range. "+ log.Debugf("Block %s is within orphan resolution range. "+
"Adding it to the orphan set and requesting its missing parents", blockHash) "Adding it to the orphan set", blockHash)
flow.addToOrphanSetAndRequestMissingParents(block, missingParents) flow.AddOrphan(block)
return nil log.Debugf("Requesting block %s missing ancestors", blockHash)
return flow.AddOrphanRootsToQueue(blockHash)
} }
// Start IBD unless we already are in IBD // Start IBD unless we already are in IBD
@@ -277,13 +305,25 @@ func (flow *handleRelayInvsFlow) isBlockInOrphanResolutionRange(blockHash *exter
return false, nil return false, nil
} }
func (flow *handleRelayInvsFlow) addToOrphanSetAndRequestMissingParents( func (flow *handleRelayInvsFlow) AddOrphanRootsToQueue(orphan *externalapi.DomainHash) error {
block *externalapi.DomainBlock, missingParents []*externalapi.DomainHash) { orphanRoots, orphanExists, err := flow.GetOrphanRoots(orphan)
if err != nil {
flow.AddOrphan(block) return err
invMessages := make([]*appmessage.MsgInvRelayBlock, len(missingParents))
for i, missingParent := range missingParents {
invMessages[i] = appmessage.NewMsgInvBlock(missingParent)
} }
if !orphanExists {
log.Infof("Orphan block %s was missing from the orphan pool while requesting for its roots. This "+
"probably happened because it was randomly evicted immediately after it was added.", orphan)
}
log.Infof("Block %s has %d missing ancestors. Adding them to the invs queue...", orphan, len(orphanRoots))
invMessages := make([]*appmessage.MsgInvRelayBlock, len(orphanRoots))
for i, root := range orphanRoots {
log.Debugf("Adding block %s missing ancestor %s to the invs queue", orphan, root)
invMessages[i] = appmessage.NewMsgInvBlock(root)
}
flow.invsQueue = append(invMessages, flow.invsQueue...) flow.invsQueue = append(invMessages, flow.invsQueue...)
return nil
} }

View File

@@ -36,6 +36,8 @@ func (flow *handleRequestBlockLocatorFlow) start() error {
if err != nil { if err != nil {
return err return err
} }
log.Debugf("Received getBlockLocator with lowHash: %s, highHash: %s, limit: %d",
lowHash, highHash, limit)
locator, err := flow.Domain().Consensus().CreateBlockLocator(lowHash, highHash, limit) locator, err := flow.Domain().Consensus().CreateBlockLocator(lowHash, highHash, limit)
if err != nil || len(locator) == 0 { if err != nil || len(locator) == 0 {

View File

@@ -17,7 +17,7 @@ type RequestIBDBlocksContext interface {
Domain() domain.Domain Domain() domain.Domain
} }
type handleRequestBlocksFlow struct { type handleRequestHeadersFlow struct {
RequestIBDBlocksContext RequestIBDBlocksContext
incomingRoute, outgoingRoute *router.Route incomingRoute, outgoingRoute *router.Route
peer *peer.Peer peer *peer.Peer
@@ -27,7 +27,7 @@ type handleRequestBlocksFlow struct {
func HandleRequestHeaders(context RequestIBDBlocksContext, incomingRoute *router.Route, func HandleRequestHeaders(context RequestIBDBlocksContext, incomingRoute *router.Route,
outgoingRoute *router.Route, peer *peer.Peer) error { outgoingRoute *router.Route, peer *peer.Peer) error {
flow := &handleRequestBlocksFlow{ flow := &handleRequestHeadersFlow{
RequestIBDBlocksContext: context, RequestIBDBlocksContext: context,
incomingRoute: incomingRoute, incomingRoute: incomingRoute,
outgoingRoute: outgoingRoute, outgoingRoute: outgoingRoute,
@@ -36,15 +36,16 @@ func HandleRequestHeaders(context RequestIBDBlocksContext, incomingRoute *router
return flow.start() return flow.start()
} }
func (flow *handleRequestBlocksFlow) start() error { func (flow *handleRequestHeadersFlow) start() error {
for { for {
lowHash, highHash, err := receiveRequestHeaders(flow.incomingRoute) lowHash, highHash, err := receiveRequestHeaders(flow.incomingRoute)
if err != nil { if err != nil {
return err return err
} }
log.Debugf("Recieved requestHeaders with lowHash: %s, highHash: %s", lowHash, highHash)
for !lowHash.Equal(highHash) { for !lowHash.Equal(highHash) {
log.Debugf("Getting block hashes between %s and %s to %s", lowHash, highHash, flow.peer) log.Debugf("Getting block headers between %s and %s to %s", lowHash, highHash, flow.peer)
// GetHashesBetween is a relatively heavy operation so we limit it // GetHashesBetween is a relatively heavy operation so we limit it
// in order to avoid locking the consensus for too long // in order to avoid locking the consensus for too long

View File

@@ -1,65 +0,0 @@
package blockrelay
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

@@ -0,0 +1,144 @@
package blockrelay
import (
"errors"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/protocol/common"
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
"github.com/kaspanet/kaspad/domain"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
)
// HandleRequestPruningPointUTXOSetAndBlockContext is the interface for the context needed for the HandleRequestPruningPointUTXOSetAndBlock flow.
type HandleRequestPruningPointUTXOSetAndBlockContext interface {
Domain() domain.Domain
}
type handleRequestPruningPointUTXOSetAndBlockFlow struct {
HandleRequestPruningPointUTXOSetAndBlockContext
incomingRoute, outgoingRoute *router.Route
}
// HandleRequestPruningPointUTXOSetAndBlock listens to appmessage.MsgRequestPruningPointUTXOSetAndBlock messages and sends
// the pruning point UTXO set and block body.
func HandleRequestPruningPointUTXOSetAndBlock(context HandleRequestPruningPointUTXOSetAndBlockContext, incomingRoute,
outgoingRoute *router.Route) error {
flow := &handleRequestPruningPointUTXOSetAndBlockFlow{
HandleRequestPruningPointUTXOSetAndBlockContext: context,
incomingRoute: incomingRoute,
outgoingRoute: outgoingRoute,
}
return flow.start()
}
func (flow *handleRequestPruningPointUTXOSetAndBlockFlow) start() error {
for {
msgRequestPruningPointUTXOSetAndBlock, err := flow.waitForRequestPruningPointUTXOSetAndBlockMessages()
if err != nil {
return err
}
err = flow.handleRequestPruningPointUTXOSetAndBlockMessage(msgRequestPruningPointUTXOSetAndBlock)
if err != nil {
return err
}
}
}
func (flow *handleRequestPruningPointUTXOSetAndBlockFlow) handleRequestPruningPointUTXOSetAndBlockMessage(
msgRequestPruningPointUTXOSetAndBlock *appmessage.MsgRequestPruningPointUTXOSetAndBlock) error {
onEnd := logger.LogAndMeasureExecutionTime(log, "handleRequestPruningPointUTXOSetAndBlockFlow")
defer onEnd()
log.Debugf("Got request for PruningPointHash UTXOSet and Block")
err := flow.sendPruningPointBlock(msgRequestPruningPointUTXOSetAndBlock)
if err != nil {
return err
}
return flow.sendPruningPointUTXOSet(msgRequestPruningPointUTXOSetAndBlock)
}
func (flow *handleRequestPruningPointUTXOSetAndBlockFlow) waitForRequestPruningPointUTXOSetAndBlockMessages() (
*appmessage.MsgRequestPruningPointUTXOSetAndBlock, error) {
message, err := flow.incomingRoute.Dequeue()
if err != nil {
return nil, err
}
msgRequestPruningPointUTXOSetAndBlock, ok := message.(*appmessage.MsgRequestPruningPointUTXOSetAndBlock)
if !ok {
return nil, protocolerrors.Errorf(true, "received unexpected message type. "+
"expected: %s, got: %s", appmessage.CmdRequestPruningPointUTXOSetAndBlock, message.Command())
}
return msgRequestPruningPointUTXOSetAndBlock, nil
}
func (flow *handleRequestPruningPointUTXOSetAndBlockFlow) sendPruningPointBlock(
msgRequestPruningPointUTXOSetAndBlock *appmessage.MsgRequestPruningPointUTXOSetAndBlock) error {
block, err := flow.Domain().Consensus().GetBlock(msgRequestPruningPointUTXOSetAndBlock.PruningPointHash)
if err != nil {
return err
}
log.Debugf("Retrieved pruning block %s", msgRequestPruningPointUTXOSetAndBlock.PruningPointHash)
return flow.outgoingRoute.Enqueue(appmessage.NewMsgIBDBlock(appmessage.DomainBlockToMsgBlock(block)))
}
func (flow *handleRequestPruningPointUTXOSetAndBlockFlow) sendPruningPointUTXOSet(
msgRequestPruningPointUTXOSetAndBlock *appmessage.MsgRequestPruningPointUTXOSetAndBlock) error {
// Send the UTXO set in `step`-sized chunks
const step = 1000
var fromOutpoint *externalapi.DomainOutpoint
chunksSent := 0
for {
pruningPointUTXOs, err := flow.Domain().Consensus().GetPruningPointUTXOs(
msgRequestPruningPointUTXOSetAndBlock.PruningPointHash, fromOutpoint, step)
if err != nil {
if errors.Is(err, ruleerrors.ErrWrongPruningPointHash) {
return flow.outgoingRoute.Enqueue(appmessage.NewMsgUnexpectedPruningPoint())
}
}
log.Debugf("Retrieved %d UTXOs for pruning block %s",
len(pruningPointUTXOs), msgRequestPruningPointUTXOSetAndBlock.PruningPointHash)
outpointAndUTXOEntryPairs :=
appmessage.DomainOutpointAndUTXOEntryPairsToOutpointAndUTXOEntryPairs(pruningPointUTXOs)
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgPruningPointUTXOSetChunk(outpointAndUTXOEntryPairs))
if err != nil {
return err
}
if len(pruningPointUTXOs) < step {
log.Debugf("Finished sending UTXOs for pruning block %s",
msgRequestPruningPointUTXOSetAndBlock.PruningPointHash)
return flow.outgoingRoute.Enqueue(appmessage.NewMsgDonePruningPointUTXOSetChunks())
}
fromOutpoint = pruningPointUTXOs[len(pruningPointUTXOs)-1].Outpoint
chunksSent++
// Wait for the peer to request more chunks every `ibdBatchSize` chunks
if chunksSent%ibdBatchSize == 0 {
message, err := flow.incomingRoute.DequeueWithTimeout(common.DefaultTimeout)
if err != nil {
return err
}
_, ok := message.(*appmessage.MsgRequestNextPruningPointUTXOSetChunk)
if !ok {
return protocolerrors.Errorf(true, "received unexpected message type. "+
"expected: %s, got: %s", appmessage.CmdRequestNextPruningPointUTXOSetChunk, message.Command())
}
}
}
}

View File

@@ -1,37 +0,0 @@
package blockrelay
import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
)
type hashesQueueSet struct {
queue []*externalapi.DomainHash
set map[externalapi.DomainHash]struct{}
}
func (r *hashesQueueSet) enqueueIfNotExists(hash *externalapi.DomainHash) {
if _, ok := r.set[*hash]; ok {
return
}
r.queue = append(r.queue, hash)
r.set[*hash] = struct{}{}
}
func (r *hashesQueueSet) dequeue(numItems int) []*externalapi.DomainHash {
var hashes []*externalapi.DomainHash
hashes, r.queue = r.queue[:numItems], r.queue[numItems:]
for _, hash := range hashes {
delete(r.set, *hash)
}
return hashes
}
func (r *hashesQueueSet) len() int {
return len(r.queue)
}
func newHashesQueueSet() *hashesQueueSet {
return &hashesQueueSet{
set: make(map[externalapi.DomainHash]struct{}),
}
}

View File

@@ -1,8 +1,13 @@
package blockrelay package blockrelay
import ( import (
"fmt"
"time" "time"
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/app/appmessage" "github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/protocol/common" "github.com/kaspanet/kaspad/app/protocol/common"
"github.com/kaspanet/kaspad/app/protocol/protocolerrors" "github.com/kaspanet/kaspad/app/protocol/protocolerrors"
@@ -13,7 +18,7 @@ import (
) )
func (flow *handleRelayInvsFlow) runIBDIfNotRunning(highHash *externalapi.DomainHash) error { func (flow *handleRelayInvsFlow) runIBDIfNotRunning(highHash *externalapi.DomainHash) error {
wasIBDNotRunning := flow.TrySetIBDRunning() wasIBDNotRunning := flow.TrySetIBDRunning(flow.peer)
if !wasIBDNotRunning { if !wasIBDNotRunning {
log.Debugf("IBD is already running") log.Debugf("IBD is already running")
return nil return nil
@@ -22,67 +27,28 @@ func (flow *handleRelayInvsFlow) runIBDIfNotRunning(highHash *externalapi.Domain
log.Debugf("IBD started with peer %s and highHash %s", flow.peer, highHash) log.Debugf("IBD started with peer %s and highHash %s", flow.peer, highHash)
// Fetch all the headers if we don't already have them log.Debugf("Syncing headers up to %s", highHash)
log.Debugf("Downloading headers up to %s", highHash) headersSynced, err := flow.syncHeaders(highHash)
err := flow.syncHeaders(highHash)
if err != nil { if err != nil {
return err return err
} }
log.Debugf("Finished downloading headers up to %s", highHash) if !headersSynced {
log.Debugf("Aborting IBD because the headers failed to sync")
return nil
}
log.Debugf("Finished syncing headers up to %s", highHash)
// Fetch the UTXO set if we don't already have it log.Debugf("Syncing the current pruning point UTXO set")
log.Debugf("Checking if there's a new pruning point under %s", highHash) syncedPruningPointUTXOSetSuccessfully, err := flow.syncPruningPointUTXOSet()
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestIBDRootHashMessage())
if err != nil { if err != nil {
return err return err
} }
if !syncedPruningPointUTXOSetSuccessfully {
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout) log.Debugf("Aborting IBD because the pruning point UTXO set failed to sync")
if err != nil { return nil
return err
} }
log.Debugf("Finished syncing the current pruning point UTXO set")
msgIBDRootHash, ok := message.(*appmessage.MsgIBDRootHashMessage)
if !ok {
return protocolerrors.Errorf(true, "received unexpected message type. "+
"expected: %s, got: %s", appmessage.CmdIBDRootHash, message.Command())
}
blockInfo, err := flow.Domain().Consensus().GetBlockInfo(msgIBDRootHash.Hash)
if err != nil {
return err
}
if blockInfo.BlockStatus == externalapi.StatusHeaderOnly {
log.Infof("Checking if the suggested pruning point %s is compatible to the node DAG", msgIBDRootHash.Hash)
isValid, err := flow.Domain().Consensus().IsValidPruningPoint(msgIBDRootHash.Hash)
if err != nil {
return err
}
if !isValid {
log.Infof("The suggested pruning point %s is incompatible to this node DAG, so stopping IBD with this"+
" peer", msgIBDRootHash.Hash)
return nil
}
log.Info("Fetching the pruning point UTXO set")
succeed, err := flow.fetchMissingUTXOSet(msgIBDRootHash.Hash)
if err != nil {
return err
}
if !succeed {
log.Infof("Couldn't successfully fetch the pruning point UTXO set. Stopping IBD.")
return nil
}
log.Info("Fetched the new pruning point UTXO set")
} else {
log.Debugf("Already has the block data of the new suggested pruning point %s", msgIBDRootHash.Hash)
}
// Fetch the block bodies
log.Debugf("Downloading block bodies up to %s", highHash) log.Debugf("Downloading block bodies up to %s", highHash)
err = flow.syncMissingBlockBodies(highHash) err = flow.syncMissingBlockBodies(highHash)
if err != nil { if err != nil {
@@ -93,87 +59,153 @@ func (flow *handleRelayInvsFlow) runIBDIfNotRunning(highHash *externalapi.Domain
return nil return nil
} }
func (flow *handleRelayInvsFlow) syncHeaders(highHash *externalapi.DomainHash) error { // syncHeaders attempts to sync headers from the peer. This method may fail
highHashReceived := false // because the peer and us have conflicting pruning points. In that case we
for !highHashReceived { // return (false, nil) so that we may stop IBD gracefully.
log.Debugf("Trying to find highest shared chain block with peer %s with high hash %s", flow.peer, highHash) func (flow *handleRelayInvsFlow) syncHeaders(highHash *externalapi.DomainHash) (bool, error) {
highestSharedBlockHash, err := flow.findHighestSharedBlockHash(highHash) log.Debugf("Trying to find highest shared chain block with peer %s with high hash %s", flow.peer, highHash)
if err != nil { highestSharedBlockHash, highestSharedBlockFound, err := flow.findHighestSharedBlockHash(highHash)
return err if err != nil {
} return false, err
log.Debugf("Found highest shared chain block %s with peer %s", highestSharedBlockHash, flow.peer)
err = flow.downloadHeaders(highestSharedBlockHash, highHash)
if err != nil {
return err
}
// We're finished once highHash has been inserted into the DAG
blockInfo, err := flow.Domain().Consensus().GetBlockInfo(highHash)
if err != nil {
return err
}
highHashReceived = blockInfo.Exists
log.Debugf("Headers downloaded from peer %s. Are further headers required: %t", flow.peer, !highHashReceived)
} }
return nil if !highestSharedBlockFound {
return false, nil
}
log.Debugf("Found highest shared chain block %s with peer %s", highestSharedBlockHash, flow.peer)
err = flow.downloadHeaders(highestSharedBlockHash, highHash)
if err != nil {
return false, err
}
// If the highHash has not been received, the peer is misbehaving
highHashBlockInfo, err := flow.Domain().Consensus().GetBlockInfo(highHash)
if err != nil {
return false, err
}
if !highHashBlockInfo.Exists {
return false, protocolerrors.Errorf(true, "did not receive "+
"highHash header %s from peer %s during header download", highHash, flow.peer)
}
log.Debugf("Headers downloaded from peer %s", flow.peer)
return true, nil
} }
func (flow *handleRelayInvsFlow) findHighestSharedBlockHash(targetHash *externalapi.DomainHash) (*externalapi.DomainHash, error) { // findHighestSharedBlock attempts to find the highest shared block between the peer
lowHash := flow.Config().ActiveNetParams.GenesisHash // and this node. This method may fail because the peer and us have conflicting pruning
highHash, err := flow.Domain().Consensus().GetHeadersSelectedTip() // points. In that case we return (nil, false, nil) so that we may stop IBD gracefully.
func (flow *handleRelayInvsFlow) findHighestSharedBlockHash(
targetHash *externalapi.DomainHash) (*externalapi.DomainHash, bool, error) {
log.Debugf("Sending a blockLocator to %s between pruning point and headers selected tip", flow.peer)
blockLocator, err := flow.Domain().Consensus().CreateFullHeadersSelectedChainBlockLocator()
if err != nil { if err != nil {
return nil, err return nil, false, err
} }
for !lowHash.Equal(highHash) { for {
log.Debugf("Sending a blockLocator to %s between %s and %s", flow.peer, lowHash, highHash) highestHash, highestHashFound, err := flow.fetchHighestHash(targetHash, blockLocator)
blockLocator, err := flow.Domain().Consensus().CreateBlockLocator(lowHash, highHash, 0)
if err != nil { if err != nil {
return nil, err return nil, false, err
}
if !highestHashFound {
return nil, false, nil
}
highestHashIndex, err := flow.findHighestHashIndex(highestHash, blockLocator)
if err != nil {
return nil, false, err
} }
ibdBlockLocatorMessage := appmessage.NewMsgIBDBlockLocator(targetHash, blockLocator) if highestHashIndex == 0 ||
err = flow.outgoingRoute.Enqueue(ibdBlockLocatorMessage) // If the block locator contains only two adjacent chain blocks, the
if err != nil { // syncer will always find the same highest chain block, so to avoid
return nil, err // an endless loop, we explicitly stop the loop in such situation.
} (len(blockLocator) == 2 && highestHashIndex == 1) {
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
if err != nil {
return nil, err
}
ibdBlockLocatorHighestHashMessage, ok := message.(*appmessage.MsgIBDBlockLocatorHighestHash)
if !ok {
return nil, protocolerrors.Errorf(true, "received unexpected message type. "+
"expected: %s, got: %s", appmessage.CmdIBDBlockLocatorHighestHash, message.Command())
}
highestHash := ibdBlockLocatorHighestHashMessage.HighestHash
log.Debugf("The highest hash the peer %s knows is %s", flow.peer, highestHash)
highestHashIndex := 0 return highestHash, true, nil
highestHashIndexFound := false
for i, blockLocatorHash := range blockLocator {
if highestHash.Equal(blockLocatorHash) {
highestHashIndex = i
highestHashIndexFound = true
break
}
} }
if !highestHashIndexFound {
return nil, protocolerrors.Errorf(true, "highest hash %s "+
"returned from peer %s is not in the original blockLocator", highestHash, flow.peer)
}
log.Debugf("The index of the highest hash in the original "+
"blockLocator sent to %s is %d", flow.peer, highestHashIndex)
locatorHashAboveHighestHash := highestHash locatorHashAboveHighestHash := highestHash
if highestHashIndex > 0 { if highestHashIndex > 0 {
locatorHashAboveHighestHash = blockLocator[highestHashIndex-1] locatorHashAboveHighestHash = blockLocator[highestHashIndex-1]
} }
highHash = locatorHashAboveHighestHash
lowHash = highestHash blockLocator, err = flow.nextBlockLocator(highestHash, locatorHashAboveHighestHash)
if err != nil {
return nil, false, err
}
}
}
func (flow *handleRelayInvsFlow) nextBlockLocator(lowHash, highHash *externalapi.DomainHash) (externalapi.BlockLocator, error) {
log.Debugf("Sending a blockLocator to %s between %s and %s", flow.peer, lowHash, highHash)
blockLocator, err := flow.Domain().Consensus().CreateHeadersSelectedChainBlockLocator(lowHash, highHash)
if err != nil {
if errors.Is(model.ErrBlockNotInSelectedParentChain, err) {
return nil, err
}
log.Debugf("Headers selected parent chain moved since findHighestSharedBlockHash - " +
"restarting with full block locator")
blockLocator, err = flow.Domain().Consensus().CreateFullHeadersSelectedChainBlockLocator()
if err != nil {
return nil, err
}
}
return blockLocator, nil
}
func (flow *handleRelayInvsFlow) findHighestHashIndex(
highestHash *externalapi.DomainHash, blockLocator externalapi.BlockLocator) (int, error) {
highestHashIndex := 0
highestHashIndexFound := false
for i, blockLocatorHash := range blockLocator {
if highestHash.Equal(blockLocatorHash) {
highestHashIndex = i
highestHashIndexFound = true
break
}
}
if !highestHashIndexFound {
return 0, protocolerrors.Errorf(true, "highest hash %s "+
"returned from peer %s is not in the original blockLocator", highestHash, flow.peer)
}
log.Debugf("The index of the highest hash in the original "+
"blockLocator sent to %s is %d", flow.peer, highestHashIndex)
return highestHashIndex, nil
}
// fetchHighestHash attempts to fetch the highest hash the peer knows amongst the given
// blockLocator. This method may fail because the peer and us have conflicting pruning
// points. In that case we return (nil, false, nil) so that we may stop IBD gracefully.
func (flow *handleRelayInvsFlow) fetchHighestHash(
targetHash *externalapi.DomainHash, blockLocator externalapi.BlockLocator) (*externalapi.DomainHash, bool, error) {
ibdBlockLocatorMessage := appmessage.NewMsgIBDBlockLocator(targetHash, blockLocator)
err := flow.outgoingRoute.Enqueue(ibdBlockLocatorMessage)
if err != nil {
return nil, false, err
}
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
if err != nil {
return nil, false, err
}
switch message := message.(type) {
case *appmessage.MsgIBDBlockLocatorHighestHash:
highestHash := message.HighestHash
log.Debugf("The highest hash the peer %s knows is %s", flow.peer, highestHash)
return highestHash, true, nil
case *appmessage.MsgIBDBlockLocatorHighestHashNotFound:
log.Debugf("Peer %s does not know any block within our blockLocator. "+
"This should only happen if there's a DAG split deeper than the pruning point.", flow.peer)
return nil, false, nil
default:
return nil, false, protocolerrors.Errorf(true, "received unexpected message type. "+
"expected: %s, got: %s", appmessage.CmdIBDBlockLocatorHighestHash, message.Command())
} }
return highHash, nil
} }
func (flow *handleRelayInvsFlow) downloadHeaders(highestSharedBlockHash *externalapi.DomainHash, func (flow *handleRelayInvsFlow) downloadHeaders(highestSharedBlockHash *externalapi.DomainHash,
@@ -189,7 +221,6 @@ func (flow *handleRelayInvsFlow) downloadHeaders(highestSharedBlockHash *externa
// headers // headers
blockHeadersMessageChan := make(chan *appmessage.BlockHeadersMessage, 2) blockHeadersMessageChan := make(chan *appmessage.BlockHeadersMessage, 2)
errChan := make(chan error) errChan := make(chan error)
doneChan := make(chan interface{})
spawn("handleRelayInvsFlow-downloadHeaders", func() { spawn("handleRelayInvsFlow-downloadHeaders", func() {
for { for {
blockHeadersMessage, doneIBD, err := flow.receiveHeaders() blockHeadersMessage, doneIBD, err := flow.receiveHeaders()
@@ -198,7 +229,7 @@ func (flow *handleRelayInvsFlow) downloadHeaders(highestSharedBlockHash *externa
return return
} }
if doneIBD { if doneIBD {
doneChan <- struct{}{} close(blockHeadersMessageChan)
return return
} }
@@ -214,7 +245,10 @@ func (flow *handleRelayInvsFlow) downloadHeaders(highestSharedBlockHash *externa
for { for {
select { select {
case blockHeadersMessage := <-blockHeadersMessageChan: case blockHeadersMessage, ok := <-blockHeadersMessageChan:
if !ok {
return nil
}
for _, header := range blockHeadersMessage.BlockHeaders { for _, header := range blockHeadersMessage.BlockHeaders {
err = flow.processHeader(header) err = flow.processHeader(header)
if err != nil { if err != nil {
@@ -223,8 +257,6 @@ func (flow *handleRelayInvsFlow) downloadHeaders(highestSharedBlockHash *externa
} }
case err := <-errChan: case err := <-errChan:
return err return err
case <-doneChan:
return nil
} }
} }
} }
@@ -274,59 +306,191 @@ func (flow *handleRelayInvsFlow) processHeader(msgBlockHeader *appmessage.MsgBlo
if !errors.As(err, &ruleerrors.RuleError{}) { if !errors.As(err, &ruleerrors.RuleError{}) {
return errors.Wrapf(err, "failed to process header %s during IBD", blockHash) return errors.Wrapf(err, "failed to process header %s during IBD", blockHash)
} }
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) if errors.Is(err, ruleerrors.ErrDuplicateBlock) {
log.Debugf("Skipping block header %s as it is a duplicate", blockHash)
} else {
log.Infof("Rejected block header %s from %s during IBD: %s", blockHash, flow.peer, err)
return protocolerrors.Wrapf(true, err, "got invalid block header %s during IBD", blockHash)
}
} }
return nil return nil
} }
func (flow *handleRelayInvsFlow) fetchMissingUTXOSet(ibdRootHash *externalapi.DomainHash) (succeed bool, err error) { func (flow *handleRelayInvsFlow) syncPruningPointUTXOSet() (bool, error) {
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestIBDRootUTXOSetAndBlock(ibdRootHash)) log.Debugf("Checking if a new pruning point is available")
err := flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestPruningPointHashMessage())
if err != nil {
return false, err
}
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
if err != nil {
return false, err
}
msgPruningPointHash, ok := message.(*appmessage.MsgPruningPointHashMessage)
if !ok {
return false, protocolerrors.Errorf(true, "received unexpected message type. "+
"expected: %s, got: %s", appmessage.CmdPruningPointHash, message.Command())
}
blockInfo, err := flow.Domain().Consensus().GetBlockInfo(msgPruningPointHash.Hash)
if err != nil { if err != nil {
return false, err return false, err
} }
utxoSet, block, found, err := flow.receiveIBDRootUTXOSetAndBlock() if !blockInfo.Exists {
return false, errors.Errorf("The pruning point header is missing")
}
if blockInfo.BlockStatus != externalapi.StatusHeaderOnly {
log.Debugf("Already has the block data of the new suggested pruning point %s", msgPruningPointHash.Hash)
return true, nil
}
log.Infof("Checking if the suggested pruning point %s is compatible to the node DAG", msgPruningPointHash.Hash)
isValid, err := flow.Domain().Consensus().IsValidPruningPoint(msgPruningPointHash.Hash)
if err != nil { if err != nil {
return false, err return false, err
} }
if !found { if !isValid {
log.Infof("The suggested pruning point %s is incompatible to this node DAG, so stopping IBD with this"+
" peer", msgPruningPointHash.Hash)
return false, nil return false, nil
} }
err = flow.Domain().Consensus().ValidateAndInsertPruningPoint(block, utxoSet) log.Info("Fetching the pruning point UTXO set")
succeed, err := flow.fetchMissingUTXOSet(msgPruningPointHash.Hash)
if err != nil {
return false, err
}
if !succeed {
log.Infof("Couldn't successfully fetch the pruning point UTXO set. Stopping IBD.")
return false, nil
}
log.Info("Fetched the new pruning point UTXO set")
return true, nil
}
func (flow *handleRelayInvsFlow) fetchMissingUTXOSet(pruningPointHash *externalapi.DomainHash) (succeed bool, err error) {
defer func() {
err := flow.Domain().Consensus().ClearImportedPruningPointData()
if err != nil {
panic(fmt.Sprintf("failed to clear imported pruning point data: %s", err))
}
}()
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestPruningPointUTXOSetAndBlock(pruningPointHash))
if err != nil {
return false, err
}
block, err := flow.receivePruningPointBlock()
if err != nil {
return false, err
}
receivedAll, err := flow.receiveAndInsertPruningPointUTXOSet(pruningPointHash)
if err != nil {
return false, err
}
if !receivedAll {
return false, nil
}
err = flow.Domain().Consensus().ValidateAndInsertImportedPruningPoint(block)
if err != nil { if err != nil {
// TODO: Find a better way to deal with finality conflicts. // TODO: Find a better way to deal with finality conflicts.
if errors.Is(err, ruleerrors.ErrSuggestedPruningViolatesFinality) { if errors.Is(err, ruleerrors.ErrSuggestedPruningViolatesFinality) {
return false, nil return false, nil
} }
return false, protocolerrors.ConvertToBanningProtocolErrorIfRuleError(err, "error with IBD root UTXO set") return false, protocolerrors.ConvertToBanningProtocolErrorIfRuleError(err, "error with pruning point UTXO set")
}
err = flow.OnPruningPointUTXOSetOverride()
if err != nil {
return false, err
} }
return true, nil return true, nil
} }
func (flow *handleRelayInvsFlow) receiveIBDRootUTXOSetAndBlock() ([]byte, *externalapi.DomainBlock, bool, error) { func (flow *handleRelayInvsFlow) receivePruningPointBlock() (*externalapi.DomainBlock, error) {
onEnd := logger.LogAndMeasureExecutionTime(log, "receivePruningPointBlock")
defer onEnd()
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout) message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
if err != nil { if err != nil {
return nil, nil, false, err return nil, err
} }
switch message := message.(type) { ibdBlockMessage, ok := message.(*appmessage.MsgIBDBlock)
case *appmessage.MsgIBDRootUTXOSetAndBlock: if !ok {
return message.UTXOSet, return nil, protocolerrors.Errorf(true, "received unexpected message type. "+
appmessage.MsgBlockToDomainBlock(message.Block), true, nil "expected: %s, got: %s", appmessage.CmdIBDBlock, message.Command())
case *appmessage.MsgIBDRootNotFound: }
return nil, nil, false, nil block := appmessage.MsgBlockToDomainBlock(ibdBlockMessage.MsgBlock)
default:
return nil, nil, false, log.Debugf("Received pruning point block %s", consensushashing.BlockHash(block))
protocolerrors.Errorf(true, "received unexpected message type. "+
"expected: %s or %s, got: %s", return block, nil
appmessage.CmdIBDRootUTXOSetAndBlock, appmessage.CmdIBDRootNotFound, message.Command(), }
func (flow *handleRelayInvsFlow) receiveAndInsertPruningPointUTXOSet(
pruningPointHash *externalapi.DomainHash) (bool, error) {
onEnd := logger.LogAndMeasureExecutionTime(log, "receiveAndInsertPruningPointUTXOSet")
defer onEnd()
receivedChunkCount := 0
receivedUTXOCount := 0
for {
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
if err != nil {
return false, err
}
switch message := message.(type) {
case *appmessage.MsgPruningPointUTXOSetChunk:
receivedUTXOCount += len(message.OutpointAndUTXOEntryPairs)
domainOutpointAndUTXOEntryPairs :=
appmessage.OutpointAndUTXOEntryPairsToDomainOutpointAndUTXOEntryPairs(message.OutpointAndUTXOEntryPairs)
err := flow.Domain().Consensus().AppendImportedPruningPointUTXOs(domainOutpointAndUTXOEntryPairs)
if err != nil {
return false, err
}
receivedChunkCount++
if receivedChunkCount%ibdBatchSize == 0 {
log.Debugf("Received %d UTXO set chunks so far, totaling in %d UTXOs",
receivedChunkCount, receivedUTXOCount)
requestNextPruningPointUTXOSetChunkMessage := appmessage.NewMsgRequestNextPruningPointUTXOSetChunk()
err := flow.outgoingRoute.Enqueue(requestNextPruningPointUTXOSetChunkMessage)
if err != nil {
return false, err
}
}
case *appmessage.MsgDonePruningPointUTXOSetChunks:
log.Infof("Finished receiving the UTXO set. Total UTXOs: %d", receivedUTXOCount)
return true, nil
case *appmessage.MsgUnexpectedPruningPoint:
log.Infof("Could not receive the next UTXO chunk because the pruning point %s "+
"is no longer the pruning point of peer %s", pruningPointHash, flow.peer)
return false, nil
default:
return false, protocolerrors.Errorf(true, "received unexpected message type. "+
"expected: %s or %s or %s, got: %s", appmessage.CmdPruningPointUTXOSetChunk,
appmessage.CmdDonePruningPointUTXOSetChunks, appmessage.CmdUnexpectedPruningPoint, message.Command(),
) )
}
} }
} }
@@ -335,6 +499,13 @@ func (flow *handleRelayInvsFlow) syncMissingBlockBodies(highHash *externalapi.Do
if err != nil { if err != nil {
return err return err
} }
if len(hashes) == 0 {
// Blocks can be inserted inside the DAG during IBD if those were requested before IBD started.
// In rare cases, all the IBD blocks might be already inserted by the time we reach this point.
// In these cases - GetMissingBlockBodyHashes would return an empty array.
log.Debugf("No missing block body hashes found.")
return nil
}
for offset := 0; offset < len(hashes); offset += ibdBatchSize { for offset := 0; offset < len(hashes); offset += ibdBatchSize {
var hashesToRequest []*externalapi.DomainHash var hashesToRequest []*externalapi.DomainHash
@@ -367,8 +538,17 @@ func (flow *handleRelayInvsFlow) syncMissingBlockBodies(highHash *externalapi.Do
return protocolerrors.Errorf(true, "expected block %s but got %s", expectedHash, blockHash) return protocolerrors.Errorf(true, "expected block %s but got %s", expectedHash, blockHash)
} }
err = flow.banIfBlockIsHeaderOnly(block)
if err != nil {
return err
}
blockInsertionResult, err := flow.Domain().Consensus().ValidateAndInsertBlock(block) blockInsertionResult, err := flow.Domain().Consensus().ValidateAndInsertBlock(block)
if err != nil { if err != nil {
if errors.Is(err, ruleerrors.ErrDuplicateBlock) {
log.Debugf("Skipping IBD Block %s as it has already been added to the DAG", blockHash)
continue
}
return protocolerrors.ConvertToBanningProtocolErrorIfRuleError(err, "invalid block %s", blockHash) return protocolerrors.ConvertToBanningProtocolErrorIfRuleError(err, "invalid block %s", blockHash)
} }
err = flow.OnNewBlock(block, blockInsertionResult) err = flow.OnNewBlock(block, blockInsertionResult)

View File

@@ -5,5 +5,5 @@ import (
"github.com/kaspanet/kaspad/util/panics" "github.com/kaspanet/kaspad/util/panics"
) )
var log, _ = logger.Get(logger.SubsystemTags.PROT) var log = logger.RegisterSubSystem("PROT")
var spawn = panics.GoroutineWrapperFunc(log) var spawn = panics.GoroutineWrapperFunc(log)

View File

@@ -0,0 +1,28 @@
package blockrelay
import (
"github.com/kaspanet/kaspad/app/appmessage"
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
"github.com/kaspanet/kaspad/domain"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
)
// SendVirtualSelectedParentInvContext is the interface for the context needed for the SendVirtualSelectedParentInv flow.
type SendVirtualSelectedParentInvContext interface {
Domain() domain.Domain
}
// SendVirtualSelectedParentInv sends a peer the selected parent hash of the virtual
func SendVirtualSelectedParentInv(context SendVirtualSelectedParentInvContext,
outgoingRoute *router.Route, peer *peerpkg.Peer) error {
virtualSelectedParent, err := context.Domain().Consensus().GetVirtualSelectedParent()
if err != nil {
return err
}
log.Debugf("Sending virtual selected parent hash %s to peer %s", virtualSelectedParent, peer)
virtualSelectedParentInv := appmessage.NewMsgInvBlock(virtualSelectedParent)
return outgoingRoute.Enqueue(virtualSelectedParentInv)
}

View File

@@ -82,14 +82,17 @@ func HandleHandshake(context HandleHandshakeContext, netConnection *netadapter.N
err := context.AddToPeers(peer) err := context.AddToPeers(peer)
if err != nil { if err != nil {
if errors.As(err, &common.ErrPeerWithSameIDExists) { if errors.Is(err, common.ErrPeerWithSameIDExists) {
return nil, protocolerrors.Wrap(false, err, "peer already exists") return nil, protocolerrors.Wrap(false, err, "peer already exists")
} }
return nil, err return nil, err
} }
if peerAddress != nil { if peerAddress != nil {
context.AddressManager().AddAddresses(peerAddress) err := context.AddressManager().AddAddresses(peerAddress)
if err != nil {
return nil, err
}
} }
return peer, nil return peer, nil
} }
@@ -104,7 +107,7 @@ func handleError(err error, flowName string, isStopping *uint32, errChan chan er
return return
} }
if protocolErr := &(protocolerrors.ProtocolError{}); errors.As(err, &protocolErr) { if protocolErr := (protocolerrors.ProtocolError{}); errors.As(err, &protocolErr) {
log.Errorf("Handshake protocol error from %s: %s", flowName, err) log.Errorf("Handshake protocol error from %s: %s", flowName, err)
if atomic.AddUint32(isStopping, 1) == 1 { if atomic.AddUint32(isStopping, 1) == 1 {
errChan <- err errChan <- err

View File

@@ -5,5 +5,5 @@ import (
"github.com/kaspanet/kaspad/util/panics" "github.com/kaspanet/kaspad/util/panics"
) )
var log, _ = logger.Get(logger.SubsystemTags.PROT) var log = logger.RegisterSubSystem("PROT")
var spawn = panics.GoroutineWrapperFunc(log) var spawn = panics.GoroutineWrapperFunc(log)

View File

@@ -42,9 +42,11 @@ func ReceiveVersion(context HandleHandshakeContext, incomingRoute *router.Route,
} }
func (flow *receiveVersionFlow) start() (*appmessage.NetAddress, error) { func (flow *receiveVersionFlow) start() (*appmessage.NetAddress, error) {
onEnd := logger.LogAndMeasureExecutionTime(log, "receiveVersionFlow.start()") onEnd := logger.LogAndMeasureExecutionTime(log, "receiveVersionFlow.start")
defer onEnd() defer onEnd()
log.Debugf("Starting receiveVersionFlow with %s", flow.peer.Address())
message, err := flow.incomingRoute.DequeueWithTimeout(common.DefaultTimeout) message, err := flow.incomingRoute.DequeueWithTimeout(common.DefaultTimeout)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -58,7 +60,7 @@ func (flow *receiveVersionFlow) start() (*appmessage.NetAddress, error) {
} }
if !allowSelfConnections && flow.NetAdapter().ID().IsEqual(msgVersion.ID) { if !allowSelfConnections && flow.NetAdapter().ID().IsEqual(msgVersion.ID) {
return nil, protocolerrors.New(true, "connected to self") return nil, protocolerrors.New(false, "connected to self")
} }
// Disconnect and ban peers from a different network // Disconnect and ban peers from a different network

View File

@@ -51,6 +51,8 @@ func (flow *sendVersionFlow) start() error {
onEnd := logger.LogAndMeasureExecutionTime(log, "sendVersionFlow.start") onEnd := logger.LogAndMeasureExecutionTime(log, "sendVersionFlow.start")
defer onEnd() defer onEnd()
log.Debugf("Starting sendVersionFlow with %s", flow.peer.Address())
// Version message. // Version message.
localAddress := flow.AddressManager().BestLocalAddress(flow.peer.Connection().NetAddress()) localAddress := flow.AddressManager().BestLocalAddress(flow.peer.Connection().NetAddress())
subnetworkID := flow.Config().SubnetworkID subnetworkID := flow.Config().SubnetworkID

View File

@@ -0,0 +1,24 @@
package testing
import (
"strings"
"testing"
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
"github.com/pkg/errors"
)
func checkFlowError(t *testing.T, err error, isProtocolError bool, shouldBan bool, contains string) {
pErr := protocolerrors.ProtocolError{}
if errors.As(err, &pErr) != isProtocolError {
t.Fatalf("Unexepcted error %+v", err)
}
if pErr.ShouldBan != shouldBan {
t.Fatalf("Exepcted shouldBan %t but got %t", shouldBan, pErr.ShouldBan)
}
if !strings.Contains(err.Error(), contains) {
t.Fatalf("Unexpected error. Expected error to contain '%s' but got: %+v", contains, err)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,50 @@
package testing
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/protocol/flows/addressexchange"
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"testing"
"time"
)
type fakeReceiveAddressesContext struct{}
func (f fakeReceiveAddressesContext) AddressManager() *addressmanager.AddressManager {
return nil
}
func TestReceiveAddressesErrors(t *testing.T) {
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
incomingRoute := router.NewRoute()
outgoingRoute := router.NewRoute()
peer := peerpkg.New(nil)
errChan := make(chan error)
go func() {
errChan <- addressexchange.ReceiveAddresses(fakeReceiveAddressesContext{}, incomingRoute, outgoingRoute, peer)
}()
_, err := outgoingRoute.DequeueWithTimeout(time.Second)
if err != nil {
t.Fatalf("DequeueWithTimeout: %+v", err)
}
// Sending addressmanager.GetAddressesMax+1 addresses should trigger a ban
err = incomingRoute.Enqueue(appmessage.NewMsgAddresses(make([]*appmessage.NetAddress,
addressmanager.GetAddressesMax+1)))
if err != nil {
t.Fatalf("Enqueue: %+v", err)
}
select {
case err := <-errChan:
checkFlowError(t, err, true, true, "address count exceeded")
case <-time.After(time.Second):
t.Fatalf("timed out after %s", time.Second)
}
})
}

View File

@@ -0,0 +1,4 @@
package testing
// Because of a bug in Go coverage fails if you have packages with test files only. See https://github.com/golang/go/issues/27333
// So this is a dummy non-test go file in the package.

View File

@@ -159,7 +159,7 @@ func (flow *handleRelayedTransactionsFlow) receiveTransactions(requestedTransact
return err return err
} }
if msgTxNotFound != nil { if msgTxNotFound != nil {
if msgTxNotFound.ID != expectedID { if !msgTxNotFound.ID.Equal(expectedID) {
return protocolerrors.Errorf(true, "expected transaction %s, but got %s", return protocolerrors.Errorf(true, "expected transaction %s, but got %s",
expectedID, msgTxNotFound.ID) expectedID, msgTxNotFound.ID)
} }
@@ -191,7 +191,7 @@ func (flow *handleRelayedTransactionsFlow) receiveTransactions(requestedTransact
continue continue
} }
return protocolerrors.Errorf(true, "rejected transaction %s", txID) return protocolerrors.Errorf(true, "rejected transaction %s: %s", txID, ruleErr)
} }
err = flow.broadcastAcceptedTransactions([]*externalapi.DomainTransactionID{txID}) err = flow.broadcastAcceptedTransactions([]*externalapi.DomainTransactionID{txID})
if err != nil { if err != nil {

View File

@@ -5,5 +5,5 @@ import (
"github.com/kaspanet/kaspad/util/panics" "github.com/kaspanet/kaspad/util/panics"
) )
var log, _ = logger.Get(logger.SubsystemTags.PROT) var log = logger.RegisterSubSystem("PROT")
var spawn = panics.GoroutineWrapperFunc(log) var spawn = panics.GoroutineWrapperFunc(log)

View File

@@ -37,6 +37,12 @@ func (m *Manager) Peers() []*peerpkg.Peer {
return m.context.Peers() return m.context.Peers()
} }
// IBDPeer returns the current IBD peer or null if the node is not
// in IBD
func (m *Manager) IBDPeer() *peerpkg.Peer {
return m.context.IBDPeer()
}
// AddTransaction adds transaction to the mempool and propagates it. // AddTransaction adds transaction to the mempool and propagates it.
func (m *Manager) AddTransaction(tx *externalapi.DomainTransaction) error { func (m *Manager) AddTransaction(tx *externalapi.DomainTransaction) error {
return m.context.AddTransaction(tx) return m.context.AddTransaction(tx)
@@ -63,6 +69,11 @@ func (m *Manager) SetOnBlockAddedToDAGHandler(onBlockAddedToDAGHandler flowconte
m.context.SetOnBlockAddedToDAGHandler(onBlockAddedToDAGHandler) m.context.SetOnBlockAddedToDAGHandler(onBlockAddedToDAGHandler)
} }
// SetOnPruningPointUTXOSetOverrideHandler sets the OnPruningPointUTXOSetOverride handler
func (m *Manager) SetOnPruningPointUTXOSetOverrideHandler(onPruningPointUTXOSetOverrideHandler flowcontext.OnPruningPointUTXOSetOverrideHandler) {
m.context.SetOnPruningPointUTXOSetOverrideHandler(onPruningPointUTXOSetOverrideHandler)
}
// SetOnTransactionAddedToMempoolHandler sets the onTransactionAddedToMempool handler // SetOnTransactionAddedToMempoolHandler sets the onTransactionAddedToMempool handler
func (m *Manager) SetOnTransactionAddedToMempoolHandler(onTransactionAddedToMempoolHandler flowcontext.OnTransactionAddedToMempoolHandler) { func (m *Manager) SetOnTransactionAddedToMempoolHandler(onTransactionAddedToMempoolHandler flowcontext.OnTransactionAddedToMempoolHandler) {
m.context.SetOnTransactionAddedToMempoolHandler(onTransactionAddedToMempoolHandler) m.context.SetOnTransactionAddedToMempoolHandler(onTransactionAddedToMempoolHandler)
@@ -73,3 +84,8 @@ func (m *Manager) SetOnTransactionAddedToMempoolHandler(onTransactionAddedToMemp
func (m *Manager) ShouldMine() (bool, error) { func (m *Manager) ShouldMine() (bool, error) {
return m.context.ShouldMine() return m.context.ShouldMine()
} }
// IsIBDRunning returns true if IBD is currently marked as running
func (m *Manager) IsIBDRunning() bool {
return m.context.IsIBDRunning()
}

View File

@@ -2,8 +2,6 @@ package peer
import ( import (
"github.com/kaspanet/kaspad/infrastructure/logger" "github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/util/panics"
) )
var log, _ = logger.Get(logger.SubsystemTags.PROT) var log = logger.RegisterSubSystem("PROT")
var spawn = panics.GoroutineWrapperFunc(log)

View File

@@ -1,9 +1,11 @@
package protocol package protocol
import ( import (
"github.com/kaspanet/kaspad/app/protocol/flows/rejects"
"sync/atomic" "sync/atomic"
"github.com/kaspanet/kaspad/app/protocol/flows/rejects"
"github.com/kaspanet/kaspad/infrastructure/network/connmanager"
"github.com/kaspanet/kaspad/app/appmessage" "github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/protocol/flows/addressexchange" "github.com/kaspanet/kaspad/app/protocol/flows/addressexchange"
"github.com/kaspanet/kaspad/app/protocol/flows/blockrelay" "github.com/kaspanet/kaspad/app/protocol/flows/blockrelay"
@@ -44,6 +46,7 @@ func (m *Manager) routerInitializer(router *routerpkg.Router, netConnection *net
panic(err) panic(err)
} }
if isBanned { if isBanned {
log.Infof("Peer %s is banned. Disconnecting...", netConnection)
netConnection.Disconnect() netConnection.Disconnect()
return return
} }
@@ -56,8 +59,20 @@ func (m *Manager) routerInitializer(router *routerpkg.Router, netConnection *net
peer, err := handshake.HandleHandshake(m.context, netConnection, receiveVersionRoute, peer, err := handshake.HandleHandshake(m.context, netConnection, receiveVersionRoute,
sendVersionRoute, router.OutgoingRoute()) sendVersionRoute, router.OutgoingRoute())
if err != nil { if err != nil {
m.handleError(err, netConnection, router.OutgoingRoute()) // non-blocking read from channel
select {
case innerError := <-errChan:
if errors.Is(err, routerpkg.ErrRouteClosed) {
m.handleError(innerError, netConnection, router.OutgoingRoute())
} else {
log.Errorf("Peer %s sent invalid message: %s", netConnection, innerError)
m.handleError(err, netConnection, router.OutgoingRoute())
}
default:
m.handleError(err, netConnection, router.OutgoingRoute())
}
return return
} }
defer m.context.RemoveFromPeers(peer) defer m.context.RemoveFromPeers(peer)
@@ -73,12 +88,12 @@ func (m *Manager) routerInitializer(router *routerpkg.Router, netConnection *net
} }
func (m *Manager) handleError(err error, netConnection *netadapter.NetConnection, outgoingRoute *routerpkg.Route) { func (m *Manager) handleError(err error, netConnection *netadapter.NetConnection, outgoingRoute *routerpkg.Route) {
if protocolErr := &(protocolerrors.ProtocolError{}); errors.As(err, &protocolErr) { if protocolErr := (protocolerrors.ProtocolError{}); errors.As(err, &protocolErr) {
if !m.context.Config().DisableBanning && protocolErr.ShouldBan { if !m.context.Config().DisableBanning && protocolErr.ShouldBan {
log.Warnf("Banning %s (reason: %s)", netConnection, protocolErr.Cause) log.Warnf("Banning %s (reason: %s)", netConnection, protocolErr.Cause)
err := m.context.ConnectionManager().Ban(netConnection) err := m.context.ConnectionManager().Ban(netConnection)
if err != nil && !errors.Is(err, addressmanager.ErrAddressNotFound) { if !errors.Is(err, connmanager.ErrCannotBanPermanent) {
panic(err) panic(err)
} }
@@ -87,6 +102,7 @@ func (m *Manager) handleError(err error, netConnection *netadapter.NetConnection
panic(err) panic(err)
} }
} }
log.Infof("Disconnecting from %s (reason: %s)", netConnection, protocolErr.Cause)
netConnection.Disconnect() netConnection.Disconnect()
return return
} }
@@ -133,10 +149,16 @@ func (m *Manager) registerBlockRelayFlows(router *routerpkg.Router, isStopping *
outgoingRoute := router.OutgoingRoute() outgoingRoute := router.OutgoingRoute()
return []*flow{ return []*flow{
m.registerOneTimeFlow("SendVirtualSelectedParentInv", router, []appmessage.MessageCommand{},
isStopping, errChan, func(route *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.SendVirtualSelectedParentInv(m.context, outgoingRoute, peer)
}),
m.registerFlow("HandleRelayInvs", router, []appmessage.MessageCommand{ m.registerFlow("HandleRelayInvs", router, []appmessage.MessageCommand{
appmessage.CmdInvRelayBlock, appmessage.CmdBlock, appmessage.CmdBlockLocator, appmessage.CmdIBDBlock, appmessage.CmdInvRelayBlock, appmessage.CmdBlock, appmessage.CmdBlockLocator, appmessage.CmdIBDBlock,
appmessage.CmdDoneHeaders, appmessage.CmdIBDRootNotFound, appmessage.CmdIBDRootUTXOSetAndBlock, appmessage.CmdDoneHeaders, appmessage.CmdUnexpectedPruningPoint, appmessage.CmdPruningPointUTXOSetChunk,
appmessage.CmdBlockHeaders, appmessage.CmdIBDRootHash, appmessage.CmdIBDBlockLocatorHighestHash}, appmessage.CmdBlockHeaders, appmessage.CmdPruningPointHash, appmessage.CmdIBDBlockLocatorHighestHash,
appmessage.CmdIBDBlockLocatorHighestHashNotFound, appmessage.CmdDonePruningPointUTXOSetChunks},
isStopping, errChan, func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error { isStopping, errChan, func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.HandleRelayInvs(m.context, incomingRoute, return blockrelay.HandleRelayInvs(m.context, incomingRoute,
outgoingRoute, peer) outgoingRoute, peer)
@@ -163,10 +185,11 @@ func (m *Manager) registerBlockRelayFlows(router *routerpkg.Router, isStopping *
}, },
), ),
m.registerFlow("HandleRequestIBDRootUTXOSetAndBlock", router, m.registerFlow("HandleRequestPruningPointUTXOSetAndBlock", router,
[]appmessage.MessageCommand{appmessage.CmdRequestIBDRootUTXOSetAndBlock}, isStopping, errChan, []appmessage.MessageCommand{appmessage.CmdRequestPruningPointUTXOSetAndBlock,
appmessage.CmdRequestNextPruningPointUTXOSetChunk}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error { func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.HandleRequestIBDRootUTXOSetAndBlock(m.context, incomingRoute, outgoingRoute) return blockrelay.HandleRequestPruningPointUTXOSetAndBlock(m.context, incomingRoute, outgoingRoute)
}, },
), ),
@@ -177,10 +200,10 @@ func (m *Manager) registerBlockRelayFlows(router *routerpkg.Router, isStopping *
}, },
), ),
m.registerFlow("HandleIBDRootHashRequests", router, m.registerFlow("HandlePruningPointHashRequests", router,
[]appmessage.MessageCommand{appmessage.CmdRequestIBDRootHash}, isStopping, errChan, []appmessage.MessageCommand{appmessage.CmdRequestPruningPointHash}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error { func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.HandleIBDRootHashRequests(m.context, incomingRoute, outgoingRoute) return blockrelay.HandlePruningPointHashRequests(m.context, incomingRoute, outgoingRoute)
}, },
), ),

View File

@@ -12,19 +12,19 @@ type ProtocolError struct {
Cause error Cause error
} }
func (e *ProtocolError) Error() string { func (e ProtocolError) Error() string {
return e.Cause.Error() return e.Cause.Error()
} }
// Unwrap returns the cause of ProtocolError, to be used with `errors.Unwrap()` // Unwrap returns the cause of ProtocolError, to be used with `errors.Unwrap()`
func (e *ProtocolError) Unwrap() error { func (e ProtocolError) Unwrap() error {
return e.Cause return e.Cause
} }
// Errorf formats according to a format specifier and returns the string // Errorf formats according to a format specifier and returns the string
// as a ProtocolError. // as a ProtocolError.
func Errorf(shouldBan bool, format string, args ...interface{}) error { func Errorf(shouldBan bool, format string, args ...interface{}) error {
return &ProtocolError{ return ProtocolError{
ShouldBan: shouldBan, ShouldBan: shouldBan,
Cause: errors.Errorf(format, args...), Cause: errors.Errorf(format, args...),
} }
@@ -33,7 +33,7 @@ func Errorf(shouldBan bool, format string, args ...interface{}) error {
// New returns a ProtocolError with the supplied message. // New returns a ProtocolError with the supplied message.
// New also records the stack trace at the point it was called. // New also records the stack trace at the point it was called.
func New(shouldBan bool, message string) error { func New(shouldBan bool, message string) error {
return &ProtocolError{ return ProtocolError{
ShouldBan: shouldBan, ShouldBan: shouldBan,
Cause: errors.New(message), Cause: errors.New(message),
} }
@@ -41,7 +41,7 @@ func New(shouldBan bool, message string) error {
// Wrap wraps the given error and returns it as a ProtocolError. // Wrap wraps the given error and returns it as a ProtocolError.
func Wrap(shouldBan bool, err error, message string) error { func Wrap(shouldBan bool, err error, message string) error {
return &ProtocolError{ return ProtocolError{
ShouldBan: shouldBan, ShouldBan: shouldBan,
Cause: errors.Wrap(err, message), Cause: errors.Wrap(err, message),
} }
@@ -49,7 +49,7 @@ func Wrap(shouldBan bool, err error, message string) error {
// Wrapf wraps the given error with the given format and returns it as a ProtocolError. // Wrapf wraps the given error with the given format and returns it as a ProtocolError.
func Wrapf(shouldBan bool, err error, format string, args ...interface{}) error { func Wrapf(shouldBan bool, err error, format string, args ...interface{}) error {
return &ProtocolError{ return ProtocolError{
ShouldBan: shouldBan, ShouldBan: shouldBan,
Cause: errors.Wrapf(err, format, args...), Cause: errors.Wrapf(err, format, args...),
} }

View File

@@ -5,5 +5,5 @@ import (
"github.com/kaspanet/kaspad/util/panics" "github.com/kaspanet/kaspad/util/panics"
) )
var log, _ = logger.Get(logger.SubsystemTags.RPCS) var log = logger.RegisterSubSystem("RPCS")
var spawn = panics.GoroutineWrapperFunc(log) var spawn = panics.GoroutineWrapperFunc(log)

View File

@@ -69,10 +69,31 @@ func (m *Manager) NotifyBlockAddedToDAG(block *externalapi.DomainBlock, blockIns
return err return err
} }
blockAddedNotification := appmessage.NewBlockAddedNotificationMessage(appmessage.DomainBlockToMsgBlock(block)) msgBlock := appmessage.DomainBlockToMsgBlock(block)
blockVerboseData, err := m.context.BuildBlockVerboseData(block.Header, block, false)
if err != nil {
return err
}
blockAddedNotification := appmessage.NewBlockAddedNotificationMessage(msgBlock, blockVerboseData)
return m.context.NotificationManager.NotifyBlockAdded(blockAddedNotification) return m.context.NotificationManager.NotifyBlockAdded(blockAddedNotification)
} }
// NotifyPruningPointUTXOSetOverride notifies the manager whenever the UTXO index
// resets due to pruning point change via IBD.
func (m *Manager) NotifyPruningPointUTXOSetOverride() error {
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyPruningPointUTXOSetOverride")
defer onEnd()
if m.context.Config.UTXOIndex {
err := m.notifyPruningPointUTXOSetOverride()
if err != nil {
return err
}
}
return nil
}
// NotifyFinalityConflict notifies the manager that there's a finality conflict in the DAG // NotifyFinalityConflict notifies the manager that there's a finality conflict in the DAG
func (m *Manager) NotifyFinalityConflict(violatingBlockHash string) error { func (m *Manager) NotifyFinalityConflict(violatingBlockHash string) error {
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyFinalityConflict") onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyFinalityConflict")
@@ -95,13 +116,25 @@ func (m *Manager) notifyUTXOsChanged(blockInsertionResult *externalapi.BlockInse
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyUTXOsChanged") onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyUTXOsChanged")
defer onEnd() defer onEnd()
utxoIndexChanges, err := m.context.UTXOIndex.Update(blockInsertionResult.VirtualSelectedParentChainChanges) utxoIndexChanges, err := m.context.UTXOIndex.Update(blockInsertionResult)
if err != nil { if err != nil {
return err return err
} }
return m.context.NotificationManager.NotifyUTXOsChanged(utxoIndexChanges) return m.context.NotificationManager.NotifyUTXOsChanged(utxoIndexChanges)
} }
func (m *Manager) notifyPruningPointUTXOSetOverride() error {
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.notifyPruningPointUTXOSetOverride")
defer onEnd()
err := m.context.UTXOIndex.Reset()
if err != nil {
return err
}
return m.context.NotificationManager.NotifyPruningPointUTXOSetOverride()
}
func (m *Manager) notifyVirtualSelectedParentBlueScoreChanged() error { func (m *Manager) notifyVirtualSelectedParentBlueScoreChanged() error {
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyVirtualSelectedParentBlueScoreChanged") onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyVirtualSelectedParentBlueScoreChanged")
defer onEnd() defer onEnd()

View File

@@ -35,9 +35,15 @@ var handlers = map[appmessage.MessageCommand]handler{
appmessage.CmdShutDownRequestMessage: rpchandlers.HandleShutDown, appmessage.CmdShutDownRequestMessage: rpchandlers.HandleShutDown,
appmessage.CmdGetHeadersRequestMessage: rpchandlers.HandleGetHeaders, appmessage.CmdGetHeadersRequestMessage: rpchandlers.HandleGetHeaders,
appmessage.CmdNotifyUTXOsChangedRequestMessage: rpchandlers.HandleNotifyUTXOsChanged, appmessage.CmdNotifyUTXOsChangedRequestMessage: rpchandlers.HandleNotifyUTXOsChanged,
appmessage.CmdStopNotifyingUTXOsChangedRequestMessage: rpchandlers.HandleStopNotifyingUTXOsChanged,
appmessage.CmdGetUTXOsByAddressesRequestMessage: rpchandlers.HandleGetUTXOsByAddresses, appmessage.CmdGetUTXOsByAddressesRequestMessage: rpchandlers.HandleGetUTXOsByAddresses,
appmessage.CmdGetVirtualSelectedParentBlueScoreRequestMessage: rpchandlers.HandleGetVirtualSelectedParentBlueScore, appmessage.CmdGetVirtualSelectedParentBlueScoreRequestMessage: rpchandlers.HandleGetVirtualSelectedParentBlueScore,
appmessage.CmdNotifyVirtualSelectedParentBlueScoreChangedRequestMessage: rpchandlers.HandleNotifyVirtualSelectedParentBlueScoreChanged, appmessage.CmdNotifyVirtualSelectedParentBlueScoreChangedRequestMessage: rpchandlers.HandleNotifyVirtualSelectedParentBlueScoreChanged,
appmessage.CmdBanRequestMessage: rpchandlers.HandleBan,
appmessage.CmdUnbanRequestMessage: rpchandlers.HandleUnban,
appmessage.CmdGetInfoRequestMessage: rpchandlers.HandleGetInfo,
appmessage.CmdNotifyPruningPointUTXOSetOverrideRequestMessage: rpchandlers.HandleNotifyPruningPointUTXOSetOverrideRequest,
appmessage.CmdStopNotifyingPruningPointUTXOSetOverrideRequestMessage: rpchandlers.HandleStopNotifyingPruningPointUTXOSetOverrideRequest,
} }
func (m *Manager) routerInitializer(router *router.Router, netConnection *netadapter.NetConnection) { func (m *Manager) routerInitializer(router *router.Router, netConnection *netadapter.NetConnection) {

View File

@@ -9,7 +9,7 @@ import (
// ConvertVirtualSelectedParentChainChangesToChainChangedNotificationMessage converts // ConvertVirtualSelectedParentChainChangesToChainChangedNotificationMessage converts
// VirtualSelectedParentChainChanges to VirtualSelectedParentChainChangedNotificationMessage // VirtualSelectedParentChainChanges to VirtualSelectedParentChainChangedNotificationMessage
func (ctx *Context) ConvertVirtualSelectedParentChainChangesToChainChangedNotificationMessage( func (ctx *Context) ConvertVirtualSelectedParentChainChangesToChainChangedNotificationMessage(
selectedParentChainChanges *externalapi.SelectedParentChainChanges) (*appmessage.VirtualSelectedParentChainChangedNotificationMessage, error) { selectedParentChainChanges *externalapi.SelectedChainPath) (*appmessage.VirtualSelectedParentChainChangedNotificationMessage, error) {
removedChainBlockHashes := make([]string, len(selectedParentChainChanges.Removed)) removedChainBlockHashes := make([]string, len(selectedParentChainChanges.Removed))
for i, removed := range selectedParentChainChanges.Removed { for i, removed := range selectedParentChainChanges.Removed {

View File

@@ -2,8 +2,6 @@ package rpccontext
import ( import (
"github.com/kaspanet/kaspad/infrastructure/logger" "github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/util/panics"
) )
var log, _ = logger.Get(logger.SubsystemTags.RPCS) var log = logger.RegisterSubSystem("RPCS")
var spawn = panics.GoroutineWrapperFunc(log)

View File

@@ -30,8 +30,9 @@ type NotificationListener struct {
propagateFinalityConflictResolvedNotifications bool propagateFinalityConflictResolvedNotifications bool
propagateUTXOsChangedNotifications bool propagateUTXOsChangedNotifications bool
propagateVirtualSelectedParentBlueScoreChangedNotifications bool propagateVirtualSelectedParentBlueScoreChangedNotifications bool
propagatePruningPointUTXOSetOverrideNotifications bool
propagateUTXOsChangedNotificationAddresses []*UTXOsChangedNotificationAddress propagateUTXOsChangedNotificationAddresses map[utxoindex.ScriptPublicKeyString]*UTXOsChangedNotificationAddress
} }
// NewNotificationManager creates a new NotificationManager // NewNotificationManager creates a new NotificationManager
@@ -180,6 +181,23 @@ func (nm *NotificationManager) NotifyVirtualSelectedParentBlueScoreChanged(
return nil return nil
} }
// NotifyPruningPointUTXOSetOverride notifies the notification manager that the UTXO index
// reset due to pruning point change via IBD.
func (nm *NotificationManager) NotifyPruningPointUTXOSetOverride() error {
nm.RLock()
defer nm.RUnlock()
for router, listener := range nm.listeners {
if listener.propagatePruningPointUTXOSetOverrideNotifications {
err := router.OutgoingRoute().Enqueue(appmessage.NewPruningPointUTXOSetOverrideNotificationMessage())
if err != nil {
return err
}
}
}
return nil
}
func newNotificationListener() *NotificationListener { func newNotificationListener() *NotificationListener {
return &NotificationListener{ return &NotificationListener{
propagateBlockAddedNotifications: false, propagateBlockAddedNotifications: false,
@@ -188,6 +206,7 @@ func newNotificationListener() *NotificationListener {
propagateFinalityConflictResolvedNotifications: false, propagateFinalityConflictResolvedNotifications: false,
propagateUTXOsChangedNotifications: false, propagateUTXOsChangedNotifications: false,
propagateVirtualSelectedParentBlueScoreChangedNotifications: false, propagateVirtualSelectedParentBlueScoreChangedNotifications: false,
propagatePruningPointUTXOSetOverrideNotifications: false,
} }
} }
@@ -216,33 +235,70 @@ func (nl *NotificationListener) PropagateFinalityConflictResolvedNotifications()
} }
// PropagateUTXOsChangedNotifications instructs the listener to send UTXOs changed notifications // PropagateUTXOsChangedNotifications instructs the listener to send UTXOs changed notifications
// to the remote listener // to the remote listener for the given addresses. Subsequent calls instruct the listener to
// send UTXOs changed notifications for those addresses along with the old ones. Duplicate addresses
// are ignored.
func (nl *NotificationListener) PropagateUTXOsChangedNotifications(addresses []*UTXOsChangedNotificationAddress) { func (nl *NotificationListener) PropagateUTXOsChangedNotifications(addresses []*UTXOsChangedNotificationAddress) {
nl.propagateUTXOsChangedNotifications = true if !nl.propagateUTXOsChangedNotifications {
nl.propagateUTXOsChangedNotificationAddresses = addresses nl.propagateUTXOsChangedNotifications = true
nl.propagateUTXOsChangedNotificationAddresses =
make(map[utxoindex.ScriptPublicKeyString]*UTXOsChangedNotificationAddress, len(addresses))
}
for _, address := range addresses {
nl.propagateUTXOsChangedNotificationAddresses[address.ScriptPublicKeyString] = address
}
}
// StopPropagatingUTXOsChangedNotifications instructs the listener to stop sending UTXOs
// changed notifications to the remote listener for the given addresses. Addresses for which
// notifications are not currently sent are ignored.
func (nl *NotificationListener) StopPropagatingUTXOsChangedNotifications(addresses []*UTXOsChangedNotificationAddress) {
if !nl.propagateUTXOsChangedNotifications {
return
}
for _, address := range addresses {
delete(nl.propagateUTXOsChangedNotificationAddresses, address.ScriptPublicKeyString)
}
} }
func (nl *NotificationListener) convertUTXOChangesToUTXOsChangedNotification( func (nl *NotificationListener) convertUTXOChangesToUTXOsChangedNotification(
utxoChanges *utxoindex.UTXOChanges) *appmessage.UTXOsChangedNotificationMessage { utxoChanges *utxoindex.UTXOChanges) *appmessage.UTXOsChangedNotificationMessage {
// As an optimization, we iterate over the smaller set (O(n)) among the two below
// and check existence over the larger set (O(1))
utxoChangesSize := len(utxoChanges.Added) + len(utxoChanges.Removed)
addressesSize := len(nl.propagateUTXOsChangedNotificationAddresses)
notification := &appmessage.UTXOsChangedNotificationMessage{} notification := &appmessage.UTXOsChangedNotificationMessage{}
for _, listenerAddress := range nl.propagateUTXOsChangedNotificationAddresses { if utxoChangesSize < addressesSize {
listenerScriptPublicKeyString := listenerAddress.ScriptPublicKeyString for scriptPublicKeyString, addedPairs := range utxoChanges.Added {
if addedPairs, ok := utxoChanges.Added[listenerScriptPublicKeyString]; ok { if listenerAddress, ok := nl.propagateUTXOsChangedNotificationAddresses[scriptPublicKeyString]; ok {
notification.Added = ConvertUTXOOutpointEntryPairsToUTXOsByAddressesEntries(listenerAddress.Address, addedPairs) utxosByAddressesEntries := ConvertUTXOOutpointEntryPairsToUTXOsByAddressesEntries(listenerAddress.Address, addedPairs)
notification.Added = append(notification.Added, utxosByAddressesEntries...)
}
} }
if removedOutpoints, ok := utxoChanges.Removed[listenerScriptPublicKeyString]; ok { for scriptPublicKeyString, removedOutpoints := range utxoChanges.Removed {
for outpoint := range removedOutpoints { if listenerAddress, ok := nl.propagateUTXOsChangedNotificationAddresses[scriptPublicKeyString]; ok {
notification.Removed = append(notification.Removed, &appmessage.UTXOsByAddressesEntry{ utxosByAddressesEntries := convertUTXOOutpointsToUTXOsByAddressesEntries(listenerAddress.Address, removedOutpoints)
Address: listenerAddress.Address, notification.Removed = append(notification.Removed, utxosByAddressesEntries...)
Outpoint: &appmessage.RPCOutpoint{ }
TransactionID: outpoint.TransactionID.String(), }
Index: outpoint.Index, } else {
}, for _, listenerAddress := range nl.propagateUTXOsChangedNotificationAddresses {
}) listenerScriptPublicKeyString := listenerAddress.ScriptPublicKeyString
if addedPairs, ok := utxoChanges.Added[listenerScriptPublicKeyString]; ok {
utxosByAddressesEntries := ConvertUTXOOutpointEntryPairsToUTXOsByAddressesEntries(listenerAddress.Address, addedPairs)
notification.Added = append(notification.Added, utxosByAddressesEntries...)
}
if removedOutpoints, ok := utxoChanges.Removed[listenerScriptPublicKeyString]; ok {
utxosByAddressesEntries := convertUTXOOutpointsToUTXOsByAddressesEntries(listenerAddress.Address, removedOutpoints)
notification.Removed = append(notification.Removed, utxosByAddressesEntries...)
} }
} }
} }
return notification return notification
} }
@@ -251,3 +307,15 @@ func (nl *NotificationListener) convertUTXOChangesToUTXOsChangedNotification(
func (nl *NotificationListener) PropagateVirtualSelectedParentBlueScoreChangedNotifications() { func (nl *NotificationListener) PropagateVirtualSelectedParentBlueScoreChangedNotifications() {
nl.propagateVirtualSelectedParentBlueScoreChangedNotifications = true nl.propagateVirtualSelectedParentBlueScoreChangedNotifications = true
} }
// PropagatePruningPointUTXOSetOverrideNotifications instructs the listener to send pruning point UTXO set override notifications
// to the remote listener.
func (nl *NotificationListener) PropagatePruningPointUTXOSetOverrideNotifications() {
nl.propagatePruningPointUTXOSetOverrideNotifications = true
}
// StopPropagatingPruningPointUTXOSetOverrideNotifications instructs the listener to stop sending pruning
// point UTXO set override notifications to the remote listener.
func (nl *NotificationListener) StopPropagatingPruningPointUTXOSetOverrideNotifications() {
nl.propagatePruningPointUTXOSetOverrideNotifications = false
}

View File

@@ -2,6 +2,9 @@ package rpccontext
import ( import (
"encoding/hex" "encoding/hex"
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
"github.com/kaspanet/kaspad/util"
"github.com/pkg/errors"
"github.com/kaspanet/kaspad/app/appmessage" "github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/domain/utxoindex" "github.com/kaspanet/kaspad/domain/utxoindex"
@@ -28,3 +31,43 @@ func ConvertUTXOOutpointEntryPairsToUTXOsByAddressesEntries(address string, pair
} }
return utxosByAddressesEntries return utxosByAddressesEntries
} }
// convertUTXOOutpointsToUTXOsByAddressesEntries converts
// UTXOOutpoints to a slice of UTXOsByAddressesEntry
func convertUTXOOutpointsToUTXOsByAddressesEntries(address string, outpoints utxoindex.UTXOOutpoints) []*appmessage.UTXOsByAddressesEntry {
utxosByAddressesEntries := make([]*appmessage.UTXOsByAddressesEntry, 0, len(outpoints))
for outpoint := range outpoints {
utxosByAddressesEntries = append(utxosByAddressesEntries, &appmessage.UTXOsByAddressesEntry{
Address: address,
Outpoint: &appmessage.RPCOutpoint{
TransactionID: outpoint.TransactionID.String(),
Index: outpoint.Index,
},
})
}
return utxosByAddressesEntries
}
// ConvertAddressStringsToUTXOsChangedNotificationAddresses converts address strings
// to UTXOsChangedNotificationAddresses
func (ctx *Context) ConvertAddressStringsToUTXOsChangedNotificationAddresses(
addressStrings []string) ([]*UTXOsChangedNotificationAddress, error) {
addresses := make([]*UTXOsChangedNotificationAddress, len(addressStrings))
for i, addressString := range addressStrings {
address, err := util.DecodeAddress(addressString, ctx.Config.ActiveNetParams.Prefix)
if err != nil {
return nil, errors.Errorf("Could not decode address '%s': %s", addressString, err)
}
scriptPublicKey, err := txscript.PayToAddrScript(address)
if err != nil {
return nil, errors.Errorf("Could not create a scriptPublicKey for address '%s': %s", addressString, err)
}
scriptPublicKeyString := utxoindex.ConvertScriptPublicKeyToString(scriptPublicKey)
addresses[i] = &UTXOsChangedNotificationAddress{
Address: addressString,
ScriptPublicKeyString: scriptPublicKeyString,
}
}
return addresses, nil
}

View File

@@ -3,11 +3,15 @@ package rpccontext
import ( import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"math" "math"
"math/big" "math/big"
"strconv" "strconv"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/util/difficulty"
"github.com/pkg/errors"
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes" "github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
"github.com/kaspanet/kaspad/domain/consensus/utils/estimatedsize" "github.com/kaspanet/kaspad/domain/consensus/utils/estimatedsize"
@@ -19,18 +23,36 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing" "github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/domain/dagconfig" "github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/pointers"
) )
// BuildBlockVerboseData builds a BlockVerboseData from the given block. // ErrBuildBlockVerboseDataInvalidBlock indicates that a block that was given to BuildBlockVerboseData is invalid.
func (ctx *Context) BuildBlockVerboseData(blockHeader externalapi.BlockHeader, includeTransactionVerboseData bool) (*appmessage.BlockVerboseData, error) { var ErrBuildBlockVerboseDataInvalidBlock = errors.New("ErrBuildBlockVerboseDataInvalidBlock")
// BuildBlockVerboseData builds a BlockVerboseData from the given blockHeader.
// A block may optionally also be given if it's available in the calling context.
func (ctx *Context) BuildBlockVerboseData(blockHeader externalapi.BlockHeader, block *externalapi.DomainBlock,
includeTransactionVerboseData bool) (*appmessage.BlockVerboseData, error) {
onEnd := logger.LogAndMeasureExecutionTime(log, "BuildBlockVerboseData")
defer onEnd()
hash := consensushashing.HeaderHash(blockHeader) hash := consensushashing.HeaderHash(blockHeader)
blockInfo, err := ctx.Domain.Consensus().GetBlockInfo(hash) blockInfo, err := ctx.Domain.Consensus().GetBlockInfo(hash)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if blockInfo.BlockStatus == externalapi.StatusInvalid {
return nil, errors.Wrap(ErrBuildBlockVerboseDataInvalidBlock, "cannot build verbose data for "+
"invalid block")
}
childrenHashes, err := ctx.Domain.Consensus().GetBlockChildren(hash)
if err != nil {
return nil, err
}
result := &appmessage.BlockVerboseData{ result := &appmessage.BlockVerboseData{
Hash: hash.String(), Hash: hash.String(),
Version: blockHeader.Version(), Version: blockHeader.Version(),
@@ -39,6 +61,7 @@ func (ctx *Context) BuildBlockVerboseData(blockHeader externalapi.BlockHeader, i
AcceptedIDMerkleRoot: blockHeader.AcceptedIDMerkleRoot().String(), AcceptedIDMerkleRoot: blockHeader.AcceptedIDMerkleRoot().String(),
UTXOCommitment: blockHeader.UTXOCommitment().String(), UTXOCommitment: blockHeader.UTXOCommitment().String(),
ParentHashes: hashes.ToStrings(blockHeader.ParentHashes()), ParentHashes: hashes.ToStrings(blockHeader.ParentHashes()),
ChildrenHashes: hashes.ToStrings(childrenHashes),
Nonce: blockHeader.Nonce(), Nonce: blockHeader.Nonce(),
Time: blockHeader.TimeInMilliseconds(), Time: blockHeader.TimeInMilliseconds(),
Bits: strconv.FormatInt(int64(blockHeader.Bits()), 16), Bits: strconv.FormatInt(int64(blockHeader.Bits()), 16),
@@ -48,9 +71,11 @@ func (ctx *Context) BuildBlockVerboseData(blockHeader externalapi.BlockHeader, i
} }
if blockInfo.BlockStatus != externalapi.StatusHeaderOnly { if blockInfo.BlockStatus != externalapi.StatusHeaderOnly {
block, err := ctx.Domain.Consensus().GetBlock(hash) if block == nil {
if err != nil { block, err = ctx.Domain.Consensus().GetBlock(hash)
return nil, err if err != nil {
return nil, err
}
} }
txIDs := make([]string, len(block.Transactions)) txIDs := make([]string, len(block.Transactions))
@@ -83,7 +108,7 @@ func (ctx *Context) GetDifficultyRatio(bits uint32, params *dagconfig.Params) fl
// converted back to a number. Note this is not the same as the proof of // converted back to a number. Note this is not the same as the proof of
// work limit directly because the block difficulty is encoded in a block // work limit directly because the block difficulty is encoded in a block
// with the compact form which loses precision. // with the compact form which loses precision.
target := util.CompactToBig(bits) target := difficulty.CompactToBig(bits)
difficulty := new(big.Rat).SetFrac(params.PowMax, target) difficulty := new(big.Rat).SetFrac(params.PowMax, target)
diff, _ := difficulty.Float64() diff, _ := difficulty.Float64()
@@ -100,6 +125,9 @@ func (ctx *Context) BuildTransactionVerboseData(tx *externalapi.DomainTransactio
blockHeader externalapi.BlockHeader, blockHash string) ( blockHeader externalapi.BlockHeader, blockHash string) (
*appmessage.TransactionVerboseData, error) { *appmessage.TransactionVerboseData, error) {
onEnd := logger.LogAndMeasureExecutionTime(log, "BuildTransactionVerboseData")
defer onEnd()
var payloadHash string var payloadHash string
if tx.SubnetworkID != subnetworks.SubnetworkIDNative { if tx.SubnetworkID != subnetworks.SubnetworkIDNative {
payloadHash = tx.PayloadHash.String() payloadHash = tx.PayloadHash.String()
@@ -167,7 +195,7 @@ func (ctx *Context) buildTransactionVerboseOutputs(tx *externalapi.DomainTransac
passesFilter := len(filterAddrMap) == 0 passesFilter := len(filterAddrMap) == 0
var encodedAddr string var encodedAddr string
if addr != nil { if addr != nil {
encodedAddr = *pointers.String(addr.EncodeAddress()) encodedAddr = addr.EncodeAddress()
// If the filter doesn't already pass, make it pass if // If the filter doesn't already pass, make it pass if
// the address exists in the filter. // the address exists in the filter.
@@ -184,6 +212,7 @@ func (ctx *Context) buildTransactionVerboseOutputs(tx *externalapi.DomainTransac
output.Index = uint32(i) output.Index = uint32(i)
output.Value = transactionOutput.Value output.Value = transactionOutput.Value
output.ScriptPubKey = &appmessage.ScriptPubKeyResult{ output.ScriptPubKey = &appmessage.ScriptPubKeyResult{
Version: transactionOutput.ScriptPublicKey.Version,
Address: encodedAddr, Address: encodedAddr,
Hex: hex.EncodeToString(transactionOutput.ScriptPublicKey.Script), Hex: hex.EncodeToString(transactionOutput.ScriptPublicKey.Script),
Type: scriptClass.String(), Type: scriptClass.String(),

View File

@@ -0,0 +1,28 @@
package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"net"
)
// HandleBan handles the respectively named RPC command
func HandleBan(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
banRequest := request.(*appmessage.BanRequestMessage)
ip := net.ParseIP(banRequest.IP)
if ip == nil {
errorMessage := &appmessage.BanResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Could not parse IP %s", banRequest.IP)
return errorMessage, nil
}
err := context.ConnectionManager.BanByIP(ip)
if err != nil {
errorMessage := &appmessage.BanResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Could not ban IP: %s", err)
return errorMessage, nil
}
response := appmessage.NewBanResponseMessage()
return response, nil
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/kaspanet/kaspad/app/rpc/rpccontext" "github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/pkg/errors"
) )
// HandleGetBlock handles the respectively named RPC command // HandleGetBlock handles the respectively named RPC command
@@ -28,10 +29,16 @@ func HandleGetBlock(context *rpccontext.Context, _ *router.Router, request appme
response := appmessage.NewGetBlockResponseMessage() response := appmessage.NewGetBlockResponseMessage()
blockVerboseData, err := context.BuildBlockVerboseData(header, getBlockRequest.IncludeTransactionVerboseData) blockVerboseData, err := context.BuildBlockVerboseData(header, nil, getBlockRequest.IncludeTransactionVerboseData)
if err != nil { if err != nil {
if errors.Is(err, rpccontext.ErrBuildBlockVerboseDataInvalidBlock) {
errorMessage := &appmessage.GetBlockResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Block %s is invalid", hash)
return errorMessage, nil
}
return nil, err return nil, err
} }
response.BlockVerboseData = blockVerboseData response.BlockVerboseData = blockVerboseData
return response, nil return response, nil

View File

@@ -36,5 +36,11 @@ func HandleGetBlockDAGInfo(context *rpccontext.Context, _ *router.Router, _ appm
response.Difficulty = context.GetDifficultyRatio(virtualInfo.Bits, context.Config.ActiveNetParams) response.Difficulty = context.GetDifficultyRatio(virtualInfo.Bits, context.Config.ActiveNetParams)
response.PastMedianTime = virtualInfo.PastMedianTime response.PastMedianTime = virtualInfo.PastMedianTime
pruningPoint, err := context.Domain.Consensus().PruningPoint()
if err != nil {
return nil, err
}
response.PruningPointHash = pruningPoint.String()
return response, nil return response, nil
} }

View File

@@ -3,18 +3,104 @@ package rpchandlers
import ( import (
"github.com/kaspanet/kaspad/app/appmessage" "github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext" "github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
) )
const ( const (
// maxBlocksInGetBlocksResponse is the max amount of blocks that are // maxBlocksInGetBlocksResponse is the max amount of blocks that are
// allowed in a GetBlocksResult. // allowed in a GetBlocksResult.
maxBlocksInGetBlocksResponse = 100 maxBlocksInGetBlocksResponse = 1000
) )
// HandleGetBlocks handles the respectively named RPC command // HandleGetBlocks handles the respectively named RPC command
func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) { func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
response := &appmessage.GetBlocksResponseMessage{} getBlocksRequest := request.(*appmessage.GetBlocksRequestMessage)
response.Error = appmessage.RPCErrorf("not implemented")
// Validate that user didn't set IncludeTransactionVerboseData without setting IncludeBlockVerboseData
if !getBlocksRequest.IncludeBlockVerboseData && getBlocksRequest.IncludeTransactionVerboseData {
return &appmessage.GetBlocksResponseMessage{
Error: appmessage.RPCErrorf(
"If includeTransactionVerboseData is set, then includeBlockVerboseData must be set as well"),
}, nil
}
// Decode lowHash
// If lowHash is empty - use genesis instead.
lowHash := context.Config.ActiveNetParams.GenesisHash
if getBlocksRequest.LowHash != "" {
var err error
lowHash, err = externalapi.NewDomainHashFromString(getBlocksRequest.LowHash)
if err != nil {
return &appmessage.GetBlocksResponseMessage{
Error: appmessage.RPCErrorf("Could not decode lowHash %s: %s", getBlocksRequest.LowHash, err),
}, nil
}
blockInfo, err := context.Domain.Consensus().GetBlockInfo(lowHash)
if err != nil {
return nil, err
}
if !blockInfo.Exists {
return &appmessage.GetBlocksResponseMessage{
Error: appmessage.RPCErrorf("Could not find lowHash %s", getBlocksRequest.LowHash),
}, nil
}
}
// Get hashes between lowHash and virtualSelectedParent
virtualSelectedParent, err := context.Domain.Consensus().GetVirtualSelectedParent()
if err != nil {
return nil, err
}
blockHashes, err := context.Domain.Consensus().GetHashesBetween(
lowHash, virtualSelectedParent, maxBlocksInGetBlocksResponse)
if err != nil {
return nil, err
}
// prepend low hash to make it inclusive
blockHashes = append([]*externalapi.DomainHash{lowHash}, blockHashes...)
// If there are no maxBlocksInGetBlocksResponse between lowHash and virtualSelectedParent -
// add virtualSelectedParent's anticone
if len(blockHashes) < maxBlocksInGetBlocksResponse {
virtualSelectedParentAnticone, err := context.Domain.Consensus().Anticone(virtualSelectedParent)
if err != nil {
return nil, err
}
blockHashes = append(blockHashes, virtualSelectedParentAnticone...)
}
// Both GetHashesBetween and Anticone might return more then the allowed number of blocks, so
// trim any extra blocks.
if len(blockHashes) > maxBlocksInGetBlocksResponse {
blockHashes = blockHashes[:maxBlocksInGetBlocksResponse]
}
// Prepare the response
response := &appmessage.GetBlocksResponseMessage{
BlockHashes: hashes.ToStrings(blockHashes),
}
// Retrieve all block data in case BlockVerboseData was requested
if getBlocksRequest.IncludeBlockVerboseData {
response.BlockVerboseData = make([]*appmessage.BlockVerboseData, len(blockHashes))
for i, blockHash := range blockHashes {
blockHeader, err := context.Domain.Consensus().GetBlockHeader(blockHash)
if err != nil {
return nil, err
}
blockVerboseData, err := context.BuildBlockVerboseData(blockHeader, nil,
getBlocksRequest.IncludeTransactionVerboseData)
if err != nil {
return nil, err
}
response.BlockVerboseData[i] = blockVerboseData
}
}
return response, nil return response, nil
} }

View File

@@ -0,0 +1,144 @@
package rpchandlers_test
import (
"reflect"
"sort"
"testing"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/app/rpc/rpchandlers"
"github.com/kaspanet/kaspad/domain/consensus"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/domain/miningmanager"
"github.com/kaspanet/kaspad/infrastructure/config"
)
type fakeDomain struct {
testapi.TestConsensus
}
func (d fakeDomain) Consensus() externalapi.Consensus { return d }
func (d fakeDomain) MiningManager() miningmanager.MiningManager { return nil }
func TestHandleGetBlocks(t *testing.T) {
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
factory := consensus.NewFactory()
tc, teardown, err := factory.NewTestConsensus(params, false, "TestHandleGetBlocks")
if err != nil {
t.Fatalf("Error setting up consensus: %+v", err)
}
defer teardown(false)
fakeContext := rpccontext.Context{
Config: &config.Config{Flags: &config.Flags{NetworkFlags: config.NetworkFlags{ActiveNetParams: params}}},
Domain: fakeDomain{tc},
}
getBlocks := func(lowHash *externalapi.DomainHash) *appmessage.GetBlocksResponseMessage {
request := appmessage.GetBlocksRequestMessage{}
if lowHash != nil {
request.LowHash = lowHash.String()
}
response, err := rpchandlers.HandleGetBlocks(&fakeContext, nil, &request)
if err != nil {
t.Fatalf("Expected empty request to not fail, instead: '%v'", err)
}
return response.(*appmessage.GetBlocksResponseMessage)
}
filterAntiPast := func(povBlock *externalapi.DomainHash, slice []*externalapi.DomainHash) []*externalapi.DomainHash {
antipast := make([]*externalapi.DomainHash, 0, len(slice))
for _, blockHash := range slice {
isInPastOfPovBlock, err := tc.DAGTopologyManager().IsAncestorOf(blockHash, povBlock)
if err != nil {
t.Fatalf("Failed doing reachability check: '%v'", err)
}
if !isInPastOfPovBlock {
antipast = append(antipast, blockHash)
}
}
return antipast
}
// Create a DAG with the following structure:
// merging block
// / | \
// split1 split2 split3
// \ | /
// merging block
// / | \
// split1 split2 split3
// \ | /
// etc.
expectedOrder := make([]*externalapi.DomainHash, 0, 40)
mergingBlock := params.GenesisHash
for i := 0; i < 10; i++ {
splitBlocks := make([]*externalapi.DomainHash, 0, 3)
for j := 0; j < 3; j++ {
blockHash, _, err := tc.AddBlock([]*externalapi.DomainHash{mergingBlock}, nil, nil)
if err != nil {
t.Fatalf("Failed adding block: %v", err)
}
splitBlocks = append(splitBlocks, blockHash)
}
sort.Sort(sort.Reverse(testutils.NewTestGhostDAGSorter(splitBlocks, tc, t)))
restOfSplitBlocks, selectedParent := splitBlocks[:len(splitBlocks)-1], splitBlocks[len(splitBlocks)-1]
expectedOrder = append(expectedOrder, selectedParent)
expectedOrder = append(expectedOrder, restOfSplitBlocks...)
mergingBlock, _, err = tc.AddBlock(splitBlocks, nil, nil)
if err != nil {
t.Fatalf("Failed adding block: %v", err)
}
expectedOrder = append(expectedOrder, mergingBlock)
}
virtualSelectedParent, err := tc.GetVirtualSelectedParent()
if err != nil {
t.Fatalf("Failed getting SelectedParent: %v", err)
}
if !virtualSelectedParent.Equal(expectedOrder[len(expectedOrder)-1]) {
t.Fatalf("Expected %s to be selectedParent, instead found: %s", expectedOrder[len(expectedOrder)-1], virtualSelectedParent)
}
requestSelectedParent := getBlocks(virtualSelectedParent)
if !reflect.DeepEqual(requestSelectedParent.BlockHashes, hashes.ToStrings([]*externalapi.DomainHash{virtualSelectedParent})) {
t.Fatalf("TestHandleGetBlocks expected:\n%v\nactual:\n%v", virtualSelectedParent, requestSelectedParent.BlockHashes)
}
for i, blockHash := range expectedOrder {
expectedBlocks := filterAntiPast(blockHash, expectedOrder)
expectedBlocks = append([]*externalapi.DomainHash{blockHash}, expectedBlocks...)
actualBlocks := getBlocks(blockHash)
if !reflect.DeepEqual(actualBlocks.BlockHashes, hashes.ToStrings(expectedBlocks)) {
t.Fatalf("TestHandleGetBlocks %d \nexpected: \n%v\nactual:\n%v", i,
hashes.ToStrings(expectedBlocks), actualBlocks.BlockHashes)
}
}
// Make explicitly sure that if lowHash==highHash we get a slice with a single hash.
actualBlocks := getBlocks(virtualSelectedParent)
if !reflect.DeepEqual(actualBlocks.BlockHashes, []string{virtualSelectedParent.String()}) {
t.Fatalf("TestHandleGetBlocks expected blocks to contain just '%s', instead got: \n%v",
virtualSelectedParent, actualBlocks.BlockHashes)
}
expectedOrder = append([]*externalapi.DomainHash{params.GenesisHash}, expectedOrder...)
actualOrder := getBlocks(nil)
if !reflect.DeepEqual(actualOrder.BlockHashes, hashes.ToStrings(expectedOrder)) {
t.Fatalf("TestHandleGetBlocks \nexpected: %v \nactual:\n%v", expectedOrder, actualOrder.BlockHashes)
}
requestAllExplictly := getBlocks(params.GenesisHash)
if !reflect.DeepEqual(requestAllExplictly.BlockHashes, hashes.ToStrings(expectedOrder)) {
t.Fatalf("TestHandleGetBlocks \nexpected: \n%v\n. actual:\n%v", expectedOrder, requestAllExplictly.BlockHashes)
}
})
}

View File

@@ -9,6 +9,7 @@ import (
// HandleGetConnectedPeerInfo handles the respectively named RPC command // HandleGetConnectedPeerInfo handles the respectively named RPC command
func HandleGetConnectedPeerInfo(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) { func HandleGetConnectedPeerInfo(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
peers := context.ProtocolManager.Peers() peers := context.ProtocolManager.Peers()
ibdPeer := context.ProtocolManager.IBDPeer()
infos := make([]*appmessage.GetConnectedPeerInfoMessage, 0, len(peers)) infos := make([]*appmessage.GetConnectedPeerInfoMessage, 0, len(peers))
for _, peer := range peers { for _, peer := range peers {
info := &appmessage.GetConnectedPeerInfoMessage{ info := &appmessage.GetConnectedPeerInfoMessage{
@@ -20,6 +21,7 @@ func HandleGetConnectedPeerInfo(context *rpccontext.Context, _ *router.Router, _
UserAgent: peer.UserAgent(), UserAgent: peer.UserAgent(),
AdvertisedProtocolVersion: peer.AdvertisedProtocolVersion(), AdvertisedProtocolVersion: peer.AdvertisedProtocolVersion(),
TimeConnected: peer.TimeConnected().Milliseconds(), TimeConnected: peer.TimeConnected().Milliseconds(),
IsIBDPeer: peer == ibdPeer,
} }
infos = append(infos, info) infos = append(infos, info)
} }

View File

@@ -0,0 +1,13 @@
package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
)
// HandleGetInfo handles the respectively named RPC command
func HandleGetInfo(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
response := appmessage.NewGetInfoResponseMessage(context.NetAdapter.ID().String())
return response, nil
}

View File

@@ -3,12 +3,27 @@ package rpchandlers
import ( import (
"github.com/kaspanet/kaspad/app/appmessage" "github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext" "github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
) )
// HandleGetMempoolEntries handles the respectively named RPC command // HandleGetMempoolEntries handles the respectively named RPC command
func HandleGetMempoolEntries(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) { func HandleGetMempoolEntries(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
response := &appmessage.GetMempoolEntriesResponseMessage{}
response.Error = appmessage.RPCErrorf("not implemented") transactions := context.Domain.MiningManager().AllTransactions()
return response, nil entries := make([]*appmessage.MempoolEntry, 0, len(transactions))
for _, tx := range transactions {
transactionVerboseData, err := context.BuildTransactionVerboseData(
tx, consensushashing.TransactionID(tx).String(), nil, "")
if err != nil {
return nil, err
}
entries = append(entries, &appmessage.MempoolEntry{
Fee: tx.Fee,
TransactionVerboseData: transactionVerboseData,
})
}
return appmessage.NewGetMempoolEntriesResponseMessage(entries), nil
} }

View File

@@ -5,5 +5,5 @@ import (
"github.com/kaspanet/kaspad/util/panics" "github.com/kaspanet/kaspad/util/panics"
) )
var log, _ = logger.Get(logger.SubsystemTags.RPCS) var log = logger.RegisterSubSystem("RPCS")
var spawn = panics.GoroutineWrapperFunc(log) var spawn = panics.GoroutineWrapperFunc(log)

View File

@@ -0,0 +1,19 @@
package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
)
// HandleNotifyPruningPointUTXOSetOverrideRequest handles the respectively named RPC command
func HandleNotifyPruningPointUTXOSetOverrideRequest(context *rpccontext.Context, router *router.Router, _ appmessage.Message) (appmessage.Message, error) {
listener, err := context.NotificationManager.Listener(router)
if err != nil {
return nil, err
}
listener.PropagatePruningPointUTXOSetOverrideNotifications()
response := appmessage.NewNotifyPruningPointUTXOSetOverrideResponseMessage()
return response, nil
}

View File

@@ -3,10 +3,7 @@ package rpchandlers
import ( import (
"github.com/kaspanet/kaspad/app/appmessage" "github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext" "github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
"github.com/kaspanet/kaspad/domain/utxoindex"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/kaspanet/kaspad/util"
) )
// HandleNotifyUTXOsChanged handles the respectively named RPC command // HandleNotifyUTXOsChanged handles the respectively named RPC command
@@ -18,26 +15,11 @@ func HandleNotifyUTXOsChanged(context *rpccontext.Context, router *router.Router
} }
notifyUTXOsChangedRequest := request.(*appmessage.NotifyUTXOsChangedRequestMessage) notifyUTXOsChangedRequest := request.(*appmessage.NotifyUTXOsChangedRequestMessage)
addresses, err := context.ConvertAddressStringsToUTXOsChangedNotificationAddresses(notifyUTXOsChangedRequest.Addresses)
addresses := make([]*rpccontext.UTXOsChangedNotificationAddress, len(notifyUTXOsChangedRequest.Addresses)) if err != nil {
for i, addressString := range notifyUTXOsChangedRequest.Addresses { errorMessage := appmessage.NewNotifyUTXOsChangedResponseMessage()
address, err := util.DecodeAddress(addressString, context.Config.ActiveNetParams.Prefix) errorMessage.Error = appmessage.RPCErrorf("Parsing error: %s", err)
if err != nil { return errorMessage, nil
errorMessage := appmessage.NewNotifyUTXOsChangedResponseMessage()
errorMessage.Error = appmessage.RPCErrorf("Could not decode address '%s': %s", addressString, err)
return errorMessage, nil
}
scriptPublicKey, err := txscript.PayToAddrScript(address)
if err != nil {
errorMessage := appmessage.NewNotifyUTXOsChangedResponseMessage()
errorMessage.Error = appmessage.RPCErrorf("Could not create a scriptPublicKey for address '%s': %s", addressString, err)
return errorMessage, nil
}
scriptPublicKeyString := utxoindex.ConvertScriptPublicKeyToString(scriptPublicKey)
addresses[i] = &rpccontext.UTXOsChangedNotificationAddress{
Address: addressString,
ScriptPublicKeyString: scriptPublicKeyString,
}
} }
listener, err := context.NotificationManager.Listener(router) listener, err := context.NotificationManager.Listener(router)

View File

@@ -0,0 +1,19 @@
package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
)
// HandleStopNotifyingPruningPointUTXOSetOverrideRequest handles the respectively named RPC command
func HandleStopNotifyingPruningPointUTXOSetOverrideRequest(context *rpccontext.Context, router *router.Router, _ appmessage.Message) (appmessage.Message, error) {
listener, err := context.NotificationManager.Listener(router)
if err != nil {
return nil, err
}
listener.StopPropagatingPruningPointUTXOSetOverrideNotifications()
response := appmessage.NewStopNotifyingPruningPointUTXOSetOverrideResponseMessage()
return response, nil
}

View File

@@ -0,0 +1,33 @@
package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
)
// HandleStopNotifyingUTXOsChanged handles the respectively named RPC command
func HandleStopNotifyingUTXOsChanged(context *rpccontext.Context, router *router.Router, request appmessage.Message) (appmessage.Message, error) {
if !context.Config.UTXOIndex {
errorMessage := appmessage.NewStopNotifyingUTXOsChangedResponseMessage()
errorMessage.Error = appmessage.RPCErrorf("Method unavailable when kaspad is run without --utxoindex")
return errorMessage, nil
}
stopNotifyingUTXOsChangedRequest := request.(*appmessage.StopNotifyingUTXOsChangedRequestMessage)
addresses, err := context.ConvertAddressStringsToUTXOsChangedNotificationAddresses(stopNotifyingUTXOsChangedRequest.Addresses)
if err != nil {
errorMessage := appmessage.NewNotifyUTXOsChangedResponseMessage()
errorMessage.Error = appmessage.RPCErrorf("Parsing error: %s", err)
return errorMessage, nil
}
listener, err := context.NotificationManager.Listener(router)
if err != nil {
return nil, err
}
listener.StopPropagatingUTXOsChangedNotifications(addresses)
response := appmessage.NewStopNotifyingUTXOsChangedResponseMessage()
return response, nil
}

View File

@@ -2,6 +2,7 @@ package rpchandlers
import ( import (
"github.com/kaspanet/kaspad/app/appmessage" "github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
"github.com/kaspanet/kaspad/app/rpc/rpccontext" "github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors" "github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing" "github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
@@ -16,14 +17,24 @@ func HandleSubmitBlock(context *rpccontext.Context, _ *router.Router, request ap
msgBlock := submitBlockRequest.Block msgBlock := submitBlockRequest.Block
domainBlock := appmessage.MsgBlockToDomainBlock(msgBlock) domainBlock := appmessage.MsgBlockToDomainBlock(msgBlock)
if context.ProtocolManager.IsIBDRunning() {
return &appmessage.SubmitBlockResponseMessage{
Error: appmessage.RPCErrorf("Block not submitted - IBD is running"),
RejectReason: appmessage.RejectReasonIsInIBD,
}, nil
}
err := context.ProtocolManager.AddBlock(domainBlock) err := context.ProtocolManager.AddBlock(domainBlock)
if err != nil { if err != nil {
if !errors.As(err, &ruleerrors.RuleError{}) { isProtocolOrRuleError := errors.As(err, &ruleerrors.RuleError{}) || errors.As(err, &protocolerrors.ProtocolError{})
if !isProtocolOrRuleError {
return nil, err return nil, err
} }
errorMessage := &appmessage.SubmitBlockResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Block rejected. Reason: %s", err) return &appmessage.SubmitBlockResponseMessage{
return errorMessage, nil Error: appmessage.RPCErrorf("Block rejected. Reason: %s", err),
RejectReason: appmessage.RejectReasonBlockInvalid,
}, nil
} }
log.Infof("Accepted block %s via submitBlock", consensushashing.BlockHash(domainBlock)) log.Infof("Accepted block %s via submitBlock", consensushashing.BlockHash(domainBlock))

View File

@@ -0,0 +1,27 @@
package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"net"
)
// HandleUnban handles the respectively named RPC command
func HandleUnban(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
unbanRequest := request.(*appmessage.UnbanRequestMessage)
ip := net.ParseIP(unbanRequest.IP)
if ip == nil {
errorMessage := &appmessage.UnbanResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Could not parse IP %s", unbanRequest.IP)
return errorMessage, nil
}
err := context.AddressManager.Unban(appmessage.NewNetAddressIPPort(ip, 0, 0))
if err != nil {
errorMessage := &appmessage.UnbanResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Could not unban IP: %s", err)
return errorMessage, nil
}
response := appmessage.NewUnbanResponseMessage()
return response, nil
}

View File

@@ -13,14 +13,15 @@ test -z "$(go fmt ./...)"
golint -set_exit_status ./... golint -set_exit_status ./...
staticcheck -checks "\ 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 ./...
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" ./...
go vet -composites=false $FLAGS ./... go vet -composites=false $FLAGS ./...
go build $FLAGS -o kaspad . go build $FLAGS -o kaspad .
go test $FLAGS ./... if [ -n "${NO_PARALLEL}" ]
then
go test -parallel=1 $FLAGS ./...
else
go test $FLAGS ./...
fi

31
changelog.txt Normal file
View File

@@ -0,0 +1,31 @@
Kaspad v0.9.0 - 2021-03-04
===========================
* Merge big subdags in pick virtual parents (#1574)
* Write in the reject message the tx rejection reason (#1573)
* Add nil checks for protowire (#1570)
* Increase getBlocks limit to 1000 (#1572)
* Return RPC error if getBlock's lowHash doesn't exist (#1569)
* Add default dns-seeder to testnet (#1568)
* Fix utxoindex deserialization (#1566)
* Add pruning point hash to GetBlockDagInfo response (#1565)
* Use EmitUnpopulated so that kaspactl prints all fields, even the default ones (#1561)
* Stop logging an error whenever an RPC/P2P connection is canceled (#1562)
* Cleanup the logger and make it asynchronous (#1524)
* Close all iterators (#1542)
* Add childrenHashes to GetBlock/s RPC commands (#1560)
* Add ScriptPublicKey.Version to RPC (#1559)
* Fix the target block rate to create less bursty mining (#1554)
Kaspad v0.8.10 - 2021-02-25
===========================
* Fix bug where invalid mempool transactions were not removed (#1551)
* Add RPC reconnection to the miner (#1552)
* Remove virtual diff parents - only selectedTip is virtualDiffParent now (#1550)
* Fix UTXO index (#1548)
* Prevent fast failing (#1545)
* Increase the sleep time in kaspaminer when the node is not synced (#1544)
* Disallow header only blocks on RPC, relay and when requesting IBD full blocks (#1537)
* Make templateManager hold a DomainBlock and isSynced bool instead of a GetBlockTemplateResponseMessage (#1538)

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