Compare commits

..

42 Commits

Author SHA1 Message Date
Elichai Turkel
3b8eda2226 go test race detector in github actions at cron job (#1534)
Co-authored-by: Ori Newman <orinewman1@gmail.com>
2021-02-18 11:41:57 +02:00
Mike Zak
15785da628 Update to version 0.8.9 2021-02-17 18:23:39 +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
187 changed files with 6228 additions and 3617 deletions

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

@@ -0,0 +1,48 @@
name: Go-Race
on:
schedule:
- cron: "0 0 * * *"
workflow_dispatch:
jobs:
race_test:
runs-on: ubuntu-20.04
strategy:
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

@@ -63,8 +63,7 @@ jobs:
go-version: 1.15
- name: Create coverage file
# Because of https://github.com/golang/go/issues/27333 this seem to "fail" even though nothing is wrong, so ignore the failure
run: go test -json -covermode=atomic -coverpkg=./... -coverprofile coverage.txt ./... || true
run: go test -covermode=atomic -coverpkg=./... -coverprofile coverage.txt ./...
- name: Upload coverage file
run: bash <(curl -s https://codecov.io/bash)

View File

@@ -65,7 +65,7 @@ is used for this project.
## Documentation
The documentation is a work-in-progress.
The [documentation](https://github.com/kaspanet/docs) is a work-in-progress
## License

View File

@@ -22,6 +22,8 @@ import (
"github.com/kaspanet/kaspad/infrastructure/os/winservice"
)
const leveldbCacheSizeMiB = 256
var desiredLimits = &limits.DesiredLimits{
FileLimitWant: 2048,
FileLimitMin: 1024,
@@ -181,5 +183,5 @@ func removeDatabase(cfg *config.Config) error {
func openDB(cfg *config.Config) (database.Database, error) {
dbPath := databasePath(cfg)
log.Infof("Loading database from '%s'", dbPath)
return ldb.NewLevelDB(dbPath)
return ldb.NewLevelDB(dbPath, leveldbCacheSizeMiB)
}

View File

@@ -59,6 +59,7 @@ const (
CmdPruningPointHash
CmdIBDBlockLocator
CmdIBDBlockLocatorHighestHash
CmdIBDBlockLocatorHighestHashNotFound
CmdBlockHeaders
CmdRequestNextPruningPointUTXOSetChunk
CmdDonePruningPointUTXOSetChunks
@@ -116,6 +117,8 @@ const (
CmdNotifyUTXOsChangedRequestMessage
CmdNotifyUTXOsChangedResponseMessage
CmdUTXOsChangedNotificationMessage
CmdStopNotifyingUTXOsChangedRequestMessage
CmdStopNotifyingUTXOsChangedResponseMessage
CmdGetUTXOsByAddressesRequestMessage
CmdGetUTXOsByAddressesResponseMessage
CmdGetVirtualSelectedParentBlueScoreRequestMessage
@@ -123,6 +126,12 @@ const (
CmdNotifyVirtualSelectedParentBlueScoreChangedRequestMessage
CmdNotifyVirtualSelectedParentBlueScoreChangedResponseMessage
CmdVirtualSelectedParentBlueScoreChangedNotificationMessage
CmdBanRequestMessage
CmdBanResponseMessage
CmdUnbanRequestMessage
CmdUnbanResponseMessage
CmdGetInfoRequestMessage
CmdGetInfoResponseMessage
)
// ProtocolMessageCommandToString maps all MessageCommands to their string representation
@@ -156,6 +165,7 @@ var ProtocolMessageCommandToString = map[MessageCommand]string{
CmdPruningPointHash: "PruningPointHash",
CmdIBDBlockLocator: "IBDBlockLocator",
CmdIBDBlockLocatorHighestHash: "IBDBlockLocatorHighestHash",
CmdIBDBlockLocatorHighestHashNotFound: "IBDBlockLocatorHighestHashNotFound",
CmdBlockHeaders: "BlockHeaders",
CmdRequestNextPruningPointUTXOSetChunk: "RequestNextPruningPointUTXOSetChunk",
CmdDonePruningPointUTXOSetChunks: "DonePruningPointUTXOSetChunks",
@@ -213,6 +223,8 @@ var RPCMessageCommandToString = map[MessageCommand]string{
CmdNotifyUTXOsChangedRequestMessage: "NotifyUTXOsChangedRequest",
CmdNotifyUTXOsChangedResponseMessage: "NotifyUTXOsChangedResponse",
CmdUTXOsChangedNotificationMessage: "UTXOsChangedNotification",
CmdStopNotifyingUTXOsChangedRequestMessage: "StopNotifyingUTXOsChangedRequest",
CmdStopNotifyingUTXOsChangedResponseMessage: "StopNotifyingUTXOsChangedResponse",
CmdGetUTXOsByAddressesRequestMessage: "GetUTXOsByAddressesRequest",
CmdGetUTXOsByAddressesResponseMessage: "GetUTXOsByAddressesResponse",
CmdGetVirtualSelectedParentBlueScoreRequestMessage: "GetVirtualSelectedParentBlueScoreRequest",
@@ -220,6 +232,12 @@ var RPCMessageCommandToString = map[MessageCommand]string{
CmdNotifyVirtualSelectedParentBlueScoreChangedRequestMessage: "NotifyVirtualSelectedParentBlueScoreChangedRequest",
CmdNotifyVirtualSelectedParentBlueScoreChangedResponseMessage: "NotifyVirtualSelectedParentBlueScoreChangedResponse",
CmdVirtualSelectedParentBlueScoreChangedNotificationMessage: "VirtualSelectedParentBlueScoreChangedNotification",
CmdBanRequestMessage: "BanRequest",
CmdBanResponseMessage: "BanResponse",
CmdUnbanRequestMessage: "UnbanRequest",
CmdUnbanResponseMessage: "UnbanResponse",
CmdGetInfoRequestMessage: "GetInfoRequestMessage",
CmdGetInfoResponseMessage: "GeInfoResponseMessage",
}
// Message is an interface that describes a kaspa message. A type that

View File

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

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

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

View File

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

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

@@ -4,9 +4,9 @@ package appmessage
// its respective RPC message
type GetBlocksRequestMessage struct {
baseMessage
LowHash string
IncludeBlockHexes bool
IncludeBlockVerboseData bool
LowHash string
IncludeBlockVerboseData bool
IncludeTransactionVerboseData bool
}
// 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
func NewGetBlocksRequestMessage(lowHash string, includeBlockHexes bool, includeBlockVerboseData bool) *GetBlocksRequestMessage {
func NewGetBlocksRequestMessage(lowHash string, includeBlockVerboseData bool,
includeTransactionVerboseData bool) *GetBlocksRequestMessage {
return &GetBlocksRequestMessage{
LowHash: lowHash,
IncludeBlockHexes: includeBlockHexes,
IncludeBlockVerboseData: includeBlockVerboseData,
LowHash: lowHash,
IncludeBlockVerboseData: includeBlockVerboseData,
IncludeTransactionVerboseData: includeTransactionVerboseData,
}
}
@@ -28,7 +29,6 @@ func NewGetBlocksRequestMessage(lowHash string, includeBlockHexes bool, includeB
type GetBlocksResponseMessage struct {
baseMessage
BlockHashes []string
BlockHexes []string
BlockVerboseData []*BlockVerboseData
Error *RPCError
@@ -45,7 +45,6 @@ func NewGetBlocksResponseMessage(blockHashes []string, blockHexes []string,
return &GetBlocksResponseMessage{
BlockHashes: blockHashes,
BlockHexes: blockHexes,
BlockVerboseData: blockVerboseData,
}
}

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
type BlockAddedNotificationMessage struct {
baseMessage
Block *MsgBlock
Block *MsgBlock
BlockVerboseData *BlockVerboseData
}
// 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
func NewBlockAddedNotificationMessage(block *MsgBlock) *BlockAddedNotificationMessage {
func NewBlockAddedNotificationMessage(block *MsgBlock, blockVerboseData *BlockVerboseData) *BlockAddedNotificationMessage {
return &BlockAddedNotificationMessage{
Block: block,
Block: block,
BlockVerboseData: blockVerboseData,
}
}

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

@@ -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

@@ -90,7 +90,7 @@ func NewComponentManager(cfg *config.Config, db infrastructuredatabase.Database,
return nil, err
}
addressManager, err := addressmanager.New(addressmanager.NewConfig(cfg))
addressManager, err := addressmanager.New(addressmanager.NewConfig(cfg), db)
if err != nil {
return nil, err
}

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,7 +1,6 @@
package flowcontext
import (
"github.com/kaspanet/kaspad/app/protocol/blocklogger"
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/pkg/errors"
@@ -38,8 +37,6 @@ func (f *FlowContext) OnNewBlock(block *externalapi.DomainBlock,
}
for i, newBlock := range newBlocks {
blocklogger.LogBlock(block)
log.Debugf("OnNewBlock: passing block %s transactions to mining manager", hash)
_, err = f.Domain().MiningManager().HandleNewBlockTransactions(newBlock.Transactions)
if err != nil {

View File

@@ -194,6 +194,9 @@ func (f *FlowContext) GetOrphanRoots(orphan *externalapi.DomainHash) ([]*externa
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
}

View File

@@ -35,6 +35,5 @@ func ReceiveAddresses(context ReceiveAddressesContext, incomingRoute *router.Rou
return protocolerrors.Errorf(true, "address count exceeded %d", addressmanager.GetAddressesMax)
}
context.AddressManager().AddAddresses(msgAddresses.AddressList...)
return nil
return context.AddressManager().AddAddresses(msgAddresses.AddressList...)
}

View File

@@ -70,8 +70,14 @@ func HandleIBDBlockLocator(context HandleIBDBlockLocatorContext, incomingRoute *
}
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)
ibdBlockLocatorHighestHashNotFoundMessage := appmessage.NewMsgIBDBlockLocatorHighestHashNotFound()
err = outgoingRoute.Enqueue(ibdBlockLocatorHighestHashNotFoundMessage)
if err != nil {
return err
}
}
}
}

View File

@@ -107,6 +107,11 @@ func (flow *handleRelayInvsFlow) start() error {
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

View File

@@ -28,10 +28,14 @@ func (flow *handleRelayInvsFlow) runIBDIfNotRunning(highHash *externalapi.Domain
log.Debugf("IBD started with peer %s and highHash %s", flow.peer, highHash)
log.Debugf("Syncing headers up to %s", highHash)
err := flow.syncHeaders(highHash)
headersSynced, err := flow.syncHeaders(highHash)
if err != nil {
return err
}
if !headersSynced {
log.Debugf("Aborting IBD because the headers failed to sync")
return nil
}
log.Debugf("Finished syncing headers up to %s", highHash)
log.Debugf("Syncing the current pruning point UTXO set")
@@ -55,47 +59,61 @@ func (flow *handleRelayInvsFlow) runIBDIfNotRunning(highHash *externalapi.Domain
return nil
}
func (flow *handleRelayInvsFlow) syncHeaders(highHash *externalapi.DomainHash) error {
highHashReceived := false
for !highHashReceived {
log.Debugf("Trying to find highest shared chain block with peer %s with high hash %s", flow.peer, highHash)
highestSharedBlockHash, err := flow.findHighestSharedBlockHash(highHash)
if err != nil {
return 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)
// syncHeaders attempts to sync headers from the peer. This method may fail
// because the peer and us have conflicting pruning points. In that case we
// return (false, nil) so that we may stop IBD gracefully.
func (flow *handleRelayInvsFlow) syncHeaders(highHash *externalapi.DomainHash) (bool, error) {
log.Debugf("Trying to find highest shared chain block with peer %s with high hash %s", flow.peer, highHash)
highestSharedBlockHash, highestSharedBlockFound, err := flow.findHighestSharedBlockHash(highHash)
if err != nil {
return false, err
}
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
// and this node. 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) 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 {
return nil, err
return nil, false, err
}
for {
highestHash, err := flow.fetchHighestHash(targetHash, blockLocator)
highestHash, highestHashFound, err := flow.fetchHighestHash(targetHash, blockLocator)
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, err
return nil, false, err
}
if highestHashIndex == 0 ||
@@ -104,7 +122,7 @@ func (flow *handleRelayInvsFlow) findHighestSharedBlockHash(targetHash *external
// an endless loop, we explicitly stop the loop in such situation.
(len(blockLocator) == 2 && highestHashIndex == 1) {
return highestHash, nil
return highestHash, true, nil
}
locatorHashAboveHighestHash := highestHash
@@ -114,7 +132,7 @@ func (flow *handleRelayInvsFlow) findHighestSharedBlockHash(targetHash *external
blockLocator, err = flow.nextBlockLocator(highestHash, locatorHashAboveHighestHash)
if err != nil {
return nil, err
return nil, false, err
}
}
}
@@ -159,27 +177,35 @@ func (flow *handleRelayInvsFlow) findHighestHashIndex(
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, error) {
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, err
return nil, false, err
}
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
if err != nil {
return nil, err
return nil, false, err
}
ibdBlockLocatorHighestHashMessage, ok := message.(*appmessage.MsgIBDBlockLocatorHighestHash)
if !ok {
return nil, protocolerrors.Errorf(true, "received unexpected message type. "+
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())
}
highestHash := ibdBlockLocatorHighestHashMessage.HighestHash
log.Debugf("The highest hash the peer %s knows is %s", flow.peer, highestHash)
return highestHash, nil
}
func (flow *handleRelayInvsFlow) downloadHeaders(highestSharedBlockHash *externalapi.DomainHash,
@@ -195,7 +221,6 @@ func (flow *handleRelayInvsFlow) downloadHeaders(highestSharedBlockHash *externa
// headers
blockHeadersMessageChan := make(chan *appmessage.BlockHeadersMessage, 2)
errChan := make(chan error)
doneChan := make(chan interface{})
spawn("handleRelayInvsFlow-downloadHeaders", func() {
for {
blockHeadersMessage, doneIBD, err := flow.receiveHeaders()
@@ -204,7 +229,7 @@ func (flow *handleRelayInvsFlow) downloadHeaders(highestSharedBlockHash *externa
return
}
if doneIBD {
doneChan <- struct{}{}
close(blockHeadersMessageChan)
return
}
@@ -220,7 +245,10 @@ func (flow *handleRelayInvsFlow) downloadHeaders(highestSharedBlockHash *externa
for {
select {
case blockHeadersMessage := <-blockHeadersMessageChan:
case blockHeadersMessage, ok := <-blockHeadersMessageChan:
if !ok {
return nil
}
for _, header := range blockHeadersMessage.BlockHeaders {
err = flow.processHeader(header)
if err != nil {
@@ -229,8 +257,6 @@ func (flow *handleRelayInvsFlow) downloadHeaders(highestSharedBlockHash *externa
}
case err := <-errChan:
return err
case <-doneChan:
return nil
}
}
}
@@ -468,6 +494,13 @@ func (flow *handleRelayInvsFlow) syncMissingBlockBodies(highHash *externalapi.Do
if err != nil {
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 {
var hashesToRequest []*externalapi.DomainHash

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

@@ -89,7 +89,10 @@ func HandleHandshake(context HandleHandshakeContext, netConnection *netadapter.N
}
if peerAddress != nil {
context.AddressManager().AddAddresses(peerAddress)
err := context.AddressManager().AddAddresses(peerAddress)
if err != nil {
return nil, err
}
}
return peer, nil
}

View File

@@ -2,6 +2,10 @@ package testing
import (
"fmt"
"sync"
"testing"
"time"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/protocol/flows/blockrelay"
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
@@ -18,9 +22,6 @@ import (
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/kaspanet/kaspad/util/mstime"
"github.com/pkg/errors"
"sync"
"testing"
"time"
)
var orphanBlock = &externalapi.DomainBlock{
@@ -109,6 +110,10 @@ type fakeRelayInvsContext struct {
rwLock sync.RWMutex
}
func (f *fakeRelayInvsContext) Anticone(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
panic(errors.Errorf("called unimplemented function from test '%s'", f.testName))
}
func (f *fakeRelayInvsContext) BuildBlock(coinbaseData *externalapi.DomainCoinbaseData, transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlock, error) {
panic(errors.Errorf("called unimplemented function from test '%s'", f.testName))
}

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

@@ -2,6 +2,7 @@ package protocol
import (
"github.com/kaspanet/kaspad/app/protocol/flows/rejects"
"github.com/kaspanet/kaspad/infrastructure/network/connmanager"
"sync/atomic"
"github.com/kaspanet/kaspad/app/appmessage"
@@ -78,7 +79,11 @@ func (m *Manager) handleError(err error, netConnection *netadapter.NetConnection
if !m.context.Config().DisableBanning && protocolErr.ShouldBan {
log.Warnf("Banning %s (reason: %s)", netConnection, protocolErr.Cause)
m.context.ConnectionManager().Ban(netConnection)
err := m.context.ConnectionManager().Ban(netConnection)
if !errors.Is(err, connmanager.ErrCannotBanPermanent) {
panic(err)
}
err = outgoingRoute.Enqueue(appmessage.NewMsgReject(protocolErr.Error()))
if err != nil && !errors.Is(err, routerpkg.ErrRouteClosed) {
panic(err)
@@ -131,11 +136,16 @@ func (m *Manager) registerBlockRelayFlows(router *routerpkg.Router, isStopping *
outgoingRoute := router.OutgoingRoute()
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{
appmessage.CmdInvRelayBlock, appmessage.CmdBlock, appmessage.CmdBlockLocator, appmessage.CmdIBDBlock,
appmessage.CmdDoneHeaders, appmessage.CmdUnexpectedPruningPoint, appmessage.CmdPruningPointUTXOSetChunk,
appmessage.CmdBlockHeaders, appmessage.CmdPruningPointHash, appmessage.CmdIBDBlockLocatorHighestHash,
appmessage.CmdDonePruningPointUTXOSetChunks},
appmessage.CmdIBDBlockLocatorHighestHashNotFound, appmessage.CmdDonePruningPointUTXOSetChunks},
isStopping, errChan, func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.HandleRelayInvs(m.context, incomingRoute,
outgoingRoute, peer)

View File

@@ -69,7 +69,12 @@ func (m *Manager) NotifyBlockAddedToDAG(block *externalapi.DomainBlock, blockIns
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)
}

View File

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

View File

@@ -31,7 +31,7 @@ type NotificationListener struct {
propagateUTXOsChangedNotifications bool
propagateVirtualSelectedParentBlueScoreChangedNotifications bool
propagateUTXOsChangedNotificationAddresses []*UTXOsChangedNotificationAddress
propagateUTXOsChangedNotificationAddresses map[utxoindex.ScriptPublicKeyString]*UTXOsChangedNotificationAddress
}
// NewNotificationManager creates a new NotificationManager
@@ -216,34 +216,70 @@ func (nl *NotificationListener) PropagateFinalityConflictResolvedNotifications()
}
// 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) {
nl.propagateUTXOsChangedNotifications = true
nl.propagateUTXOsChangedNotificationAddresses = addresses
if !nl.propagateUTXOsChangedNotifications {
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(
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{}
for _, listenerAddress := range nl.propagateUTXOsChangedNotificationAddresses {
listenerScriptPublicKeyString := listenerAddress.ScriptPublicKeyString
if addedPairs, ok := utxoChanges.Added[listenerScriptPublicKeyString]; ok {
notification.Added = append(notification.Added,
ConvertUTXOOutpointEntryPairsToUTXOsByAddressesEntries(listenerAddress.Address, addedPairs)...)
if utxoChangesSize < addressesSize {
for scriptPublicKeyString, addedPairs := range utxoChanges.Added {
if listenerAddress, ok := nl.propagateUTXOsChangedNotificationAddresses[scriptPublicKeyString]; ok {
utxosByAddressesEntries := ConvertUTXOOutpointEntryPairsToUTXOsByAddressesEntries(listenerAddress.Address, addedPairs)
notification.Added = append(notification.Added, utxosByAddressesEntries...)
}
}
if removedOutpoints, ok := utxoChanges.Removed[listenerScriptPublicKeyString]; ok {
for outpoint := range removedOutpoints {
notification.Removed = append(notification.Removed, &appmessage.UTXOsByAddressesEntry{
Address: listenerAddress.Address,
Outpoint: &appmessage.RPCOutpoint{
TransactionID: outpoint.TransactionID.String(),
Index: outpoint.Index,
},
})
for scriptPublicKeyString, removedOutpoints := range utxoChanges.Removed {
if listenerAddress, ok := nl.propagateUTXOsChangedNotificationAddresses[scriptPublicKeyString]; ok {
utxosByAddressesEntries := convertUTXOOutpointsToUTXOsByAddressesEntries(listenerAddress.Address, removedOutpoints)
notification.Removed = append(notification.Removed, utxosByAddressesEntries...)
}
}
} 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
}

View File

@@ -2,6 +2,9 @@ package rpccontext
import (
"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/domain/utxoindex"
@@ -28,3 +31,43 @@ func ConvertUTXOOutpointEntryPairsToUTXOsByAddressesEntries(address string, pair
}
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

@@ -4,7 +4,9 @@ import (
"encoding/hex"
"fmt"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/util/difficulty"
"github.com/pkg/errors"
"math"
"math/big"
"strconv"
@@ -20,17 +22,31 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/util/pointers"
)
// BuildBlockVerboseData builds a BlockVerboseData from the given block.
func (ctx *Context) BuildBlockVerboseData(blockHeader externalapi.BlockHeader, includeTransactionVerboseData bool) (*appmessage.BlockVerboseData, error) {
// ErrBuildBlockVerboseDataInvalidBlock indicates that a block that was given to BuildBlockVerboseData is invalid.
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)
blockInfo, err := ctx.Domain.Consensus().GetBlockInfo(hash)
if err != nil {
return nil, err
}
if blockInfo.BlockStatus == externalapi.StatusInvalid {
return nil, errors.Wrap(ErrBuildBlockVerboseDataInvalidBlock, "cannot build verbose data for "+
"invalid block")
}
result := &appmessage.BlockVerboseData{
Hash: hash.String(),
Version: blockHeader.Version(),
@@ -48,9 +64,11 @@ func (ctx *Context) BuildBlockVerboseData(blockHeader externalapi.BlockHeader, i
}
if blockInfo.BlockStatus != externalapi.StatusHeaderOnly {
block, err := ctx.Domain.Consensus().GetBlock(hash)
if err != nil {
return nil, err
if block == nil {
block, err = ctx.Domain.Consensus().GetBlock(hash)
if err != nil {
return nil, err
}
}
txIDs := make([]string, len(block.Transactions))
@@ -100,6 +118,9 @@ func (ctx *Context) BuildTransactionVerboseData(tx *externalapi.DomainTransactio
blockHeader externalapi.BlockHeader, blockHash string) (
*appmessage.TransactionVerboseData, error) {
onEnd := logger.LogAndMeasureExecutionTime(log, "BuildTransactionVerboseData")
defer onEnd()
var payloadHash string
if tx.SubnetworkID != subnetworks.SubnetworkIDNative {
payloadHash = tx.PayloadHash.String()
@@ -167,7 +188,7 @@ func (ctx *Context) buildTransactionVerboseOutputs(tx *externalapi.DomainTransac
passesFilter := len(filterAddrMap) == 0
var encodedAddr string
if addr != nil {
encodedAddr = *pointers.String(addr.EncodeAddress())
encodedAddr = addr.EncodeAddress()
// If the filter doesn't already pass, make it pass if
// the address exists in the filter.

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/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/pkg/errors"
)
// HandleGetBlock handles the respectively named RPC command
@@ -28,10 +29,16 @@ func HandleGetBlock(context *rpccontext.Context, _ *router.Router, request appme
response := appmessage.NewGetBlockResponseMessage()
blockVerboseData, err := context.BuildBlockVerboseData(header, getBlockRequest.IncludeTransactionVerboseData)
blockVerboseData, err := context.BuildBlockVerboseData(header, nil, getBlockRequest.IncludeTransactionVerboseData)
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
}
response.BlockVerboseData = blockVerboseData
return response, nil

View File

@@ -3,6 +3,8 @@ package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"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"
)
@@ -14,7 +16,80 @@ const (
// HandleGetBlocks handles the respectively named RPC command
func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
response := &appmessage.GetBlocksResponseMessage{}
response.Error = appmessage.RPCErrorf("not implemented")
getBlocksRequest := request.(*appmessage.GetBlocksRequestMessage)
// 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
}
}
// 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
}

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

@@ -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,10 +3,7 @@ package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"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/util"
)
// HandleNotifyUTXOsChanged handles the respectively named RPC command
@@ -18,26 +15,11 @@ func HandleNotifyUTXOsChanged(context *rpccontext.Context, router *router.Router
}
notifyUTXOsChangedRequest := request.(*appmessage.NotifyUTXOsChangedRequestMessage)
addresses := make([]*rpccontext.UTXOsChangedNotificationAddress, len(notifyUTXOsChangedRequest.Addresses))
for i, addressString := range notifyUTXOsChangedRequest.Addresses {
address, err := util.DecodeAddress(addressString, context.Config.ActiveNetParams.Prefix)
if err != 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,
}
addresses, err := context.ConvertAddressStringsToUTXOsChangedNotificationAddresses(notifyUTXOsChangedRequest.Addresses)
if err != nil {
errorMessage := appmessage.NewNotifyUTXOsChangedResponseMessage()
errorMessage.Error = appmessage.RPCErrorf("Parsing error: %s", err)
return errorMessage, nil
}
listener, err := context.NotificationManager.Listener(router)

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

@@ -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,6 +13,7 @@ var commandTypes = []reflect.Type{
reflect.TypeOf(protowire.KaspadMessage_GetConnectedPeerInfoRequest{}),
reflect.TypeOf(protowire.KaspadMessage_GetPeerAddressesRequest{}),
reflect.TypeOf(protowire.KaspadMessage_GetCurrentNetworkRequest{}),
reflect.TypeOf(protowire.KaspadMessage_GetInfoRequest{}),
reflect.TypeOf(protowire.KaspadMessage_GetBlockRequest{}),
reflect.TypeOf(protowire.KaspadMessage_GetBlocksRequest{}),
@@ -32,6 +33,9 @@ var commandTypes = []reflect.Type{
reflect.TypeOf(protowire.KaspadMessage_SubmitTransactionRequest{}),
reflect.TypeOf(protowire.KaspadMessage_GetUtxosByAddressesRequest{}),
reflect.TypeOf(protowire.KaspadMessage_BanRequest{}),
reflect.TypeOf(protowire.KaspadMessage_UnbanRequest{}),
}
type commandDescription struct {

View File

@@ -77,6 +77,10 @@ func parseConfig() (*configFlags, error) {
}
}
if cfg.MiningAddr == "" {
return nil, errors.New("--miningaddr is required")
}
initLog(defaultLogFile, defaultErrLogFile)
return cfg, nil

View File

@@ -2,13 +2,13 @@ package main
import (
nativeerrors "errors"
"github.com/kaspanet/kaspad/cmd/kaspaminer/templatemanager"
"github.com/kaspanet/kaspad/domain/consensus/model/pow"
"github.com/kaspanet/kaspad/util/difficulty"
"math/rand"
"sync/atomic"
"time"
"github.com/kaspanet/kaspad/domain/consensus/model/pow"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
@@ -29,11 +29,19 @@ func mineLoop(client *minerClient, numberOfBlocks uint64, targetBlocksPerSecond
rand.Seed(time.Now().UnixNano()) // Seed the global concurrent-safe random source.
errChan := make(chan error)
templateStopChan := make(chan struct{})
doneChan := make(chan struct{})
spawn("mineLoop-internalLoop", func() {
// We don't want to send router.DefaultMaxMessages blocks at once because there's
// a high chance we'll get disconnected from the node, so we make the channel
// capacity router.DefaultMaxMessages/2 (we give some slack for getBlockTemplate
// requests)
foundBlockChan := make(chan *externalapi.DomainBlock, router.DefaultMaxMessages/2)
spawn("templatesLoop", func() {
templatesLoop(client, miningAddr, errChan)
})
spawn("blocksLoop", func() {
const windowSize = 10
var expectedDurationForWindow time.Duration
var windowExpectedEndTime time.Time
@@ -44,16 +52,8 @@ func mineLoop(client *minerClient, numberOfBlocks uint64, targetBlocksPerSecond
}
blockInWindowIndex := 0
for i := uint64(0); numberOfBlocks == 0 || i < numberOfBlocks; i++ {
foundBlock := make(chan *externalapi.DomainBlock)
mineNextBlock(client, miningAddr, foundBlock, mineWhenNotSynced, templateStopChan, errChan)
block := <-foundBlock
templateStopChan <- struct{}{}
err := handleFoundBlock(client, block)
if err != nil {
errChan <- err
}
for {
foundBlockChan <- mineNextBlock(mineWhenNotSynced)
if hasBlockRateTarget {
blockInWindowIndex++
@@ -70,6 +70,17 @@ func mineLoop(client *minerClient, numberOfBlocks uint64, targetBlocksPerSecond
}
}
})
spawn("handleFoundBlock", func() {
for i := uint64(0); numberOfBlocks == 0 || i < numberOfBlocks; i++ {
block := <-foundBlockChan
err := handleFoundBlock(client, block)
if err != nil {
errChan <- err
return
}
}
doneChan <- struct{}{}
})
@@ -99,21 +110,9 @@ func logHashRate() {
})
}
func mineNextBlock(client *minerClient, miningAddr util.Address, foundBlock chan *externalapi.DomainBlock, mineWhenNotSynced bool,
templateStopChan chan struct{}, errChan chan error) {
newTemplateChan := make(chan *appmessage.GetBlockTemplateResponseMessage)
spawn("templatesLoop", func() {
templatesLoop(client, miningAddr, newTemplateChan, errChan, templateStopChan)
})
spawn("solveLoop", func() {
solveLoop(newTemplateChan, foundBlock, mineWhenNotSynced)
})
}
func handleFoundBlock(client *minerClient, block *externalapi.DomainBlock) error {
blockHash := consensushashing.BlockHash(block)
log.Infof("Found block %s with parents %s. Submitting to %s", blockHash, block.Header.ParentHashes(), client.Address())
log.Infof("Submitting block %s to %s", blockHash, client.Address())
rejectReason, err := client.SubmitBlock(block)
if err != nil {
@@ -132,48 +131,73 @@ func handleFoundBlock(client *minerClient, block *externalapi.DomainBlock) error
return nil
}
func solveBlock(block *externalapi.DomainBlock, stopChan chan struct{}, foundBlock chan *externalapi.DomainBlock) {
targetDifficulty := difficulty.CompactToBig(block.Header.Bits())
headerForMining := block.Header.ToMutable()
initialNonce := rand.Uint64() // Use the global concurrent-safe random source.
for i := initialNonce; i != initialNonce-1; i++ {
select {
case <-stopChan:
return
default:
headerForMining.SetNonce(i)
atomic.AddUint64(&hashesTried, 1)
if pow.CheckProofOfWorkWithTarget(headerForMining, targetDifficulty) {
block.Header = headerForMining.ToImmutable()
foundBlock <- block
return
}
func mineNextBlock(mineWhenNotSynced bool) *externalapi.DomainBlock {
nonce := rand.Uint64() // Use the global concurrent-safe random source.
for {
nonce++
// For each nonce we try to build a block from the most up to date
// block template.
// In the rare case where the nonce space is exhausted for a specific
// block, it'll keep looping the nonce until a new block template
// is discovered.
block := getBlockForMining(mineWhenNotSynced)
targetDifficulty := difficulty.CompactToBig(block.Header.Bits())
headerForMining := block.Header.ToMutable()
headerForMining.SetNonce(nonce)
atomic.AddUint64(&hashesTried, 1)
if pow.CheckProofOfWorkWithTarget(headerForMining, targetDifficulty) {
block.Header = headerForMining.ToImmutable()
log.Infof("Found block %s with parents %s", consensushashing.BlockHash(block), block.Header.ParentHashes())
return block
}
}
}
func templatesLoop(client *minerClient, miningAddr util.Address,
newTemplateChan chan *appmessage.GetBlockTemplateResponseMessage, errChan chan error, stopChan chan struct{}) {
func getBlockForMining(mineWhenNotSynced bool) *externalapi.DomainBlock {
tryCount := 0
for {
tryCount++
const sleepTime = 500 * time.Millisecond
shouldLog := (tryCount-1)%10 == 0
template := templatemanager.Get()
if template == nil {
if shouldLog {
log.Info("Waiting for the initial template")
}
time.Sleep(sleepTime)
continue
}
if !template.IsSynced && !mineWhenNotSynced {
if shouldLog {
log.Warnf("Kaspad is not synced. Skipping current block template")
}
time.Sleep(sleepTime)
continue
}
return appmessage.MsgBlockToDomainBlock(template.MsgBlock)
}
}
func templatesLoop(client *minerClient, miningAddr util.Address, errChan chan error) {
getBlockTemplate := func() {
template, err := client.GetBlockTemplate(miningAddr.String())
if nativeerrors.Is(err, router.ErrTimeout) {
log.Warnf("Got timeout while requesting block template from %s: %s", client.Address(), err)
return
} else if err != nil {
}
if err != nil {
errChan <- errors.Errorf("Error getting block template from %s: %s", client.Address(), err)
return
}
newTemplateChan <- template
templatemanager.Set(template)
}
getBlockTemplate()
const tickerTime = 500 * time.Millisecond
ticker := time.NewTicker(tickerTime)
for {
select {
case <-stopChan:
close(newTemplateChan)
return
case <-client.blockAddedNotificationChan:
getBlockTemplate()
ticker.Reset(tickerTime)
@@ -182,30 +206,3 @@ func templatesLoop(client *minerClient, miningAddr util.Address,
}
}
}
func solveLoop(newTemplateChan chan *appmessage.GetBlockTemplateResponseMessage, foundBlock chan *externalapi.DomainBlock,
mineWhenNotSynced bool) {
var stopOldTemplateSolving chan struct{}
for template := range newTemplateChan {
if !template.IsSynced && !mineWhenNotSynced {
log.Warnf("Kaspad is not synced. Skipping current block template")
continue
}
if stopOldTemplateSolving != nil {
close(stopOldTemplateSolving)
}
stopOldTemplateSolving = make(chan struct{})
block := appmessage.MsgBlockToDomainBlock(template.MsgBlock)
stopOldTemplateSolvingCopy := stopOldTemplateSolving
spawn("solveBlock", func() {
solveBlock(block, stopOldTemplateSolvingCopy, foundBlock)
})
}
if stopOldTemplateSolving != nil {
close(stopOldTemplateSolving)
}
}

View File

@@ -0,0 +1,23 @@
package templatemanager
import (
"github.com/kaspanet/kaspad/app/appmessage"
"sync"
)
var currentTemplate *appmessage.GetBlockTemplateResponseMessage
var lock = &sync.Mutex{}
// Get returns the template to work on
func Get() *appmessage.GetBlockTemplateResponseMessage {
lock.Lock()
defer lock.Unlock()
return currentTemplate
}
// Set sets the current template to work on
func Set(template *appmessage.GetBlockTemplateResponseMessage) {
lock.Lock()
defer lock.Unlock()
currentTemplate = template
}

View File

@@ -27,7 +27,7 @@ WORKDIR /app
RUN apk add --no-cache ca-certificates tini
COPY --from=build /go/src/github.com/kaspanet/kaspad/kaspad /app/
COPY --from=build /go/src/github.com/kaspanet/kaspad/sample-kaspad.conf /app/
COPY --from=build /go/src/github.com/kaspanet/kaspad/infrastructure/config/sample-kaspad.conf /app/
USER nobody
ENTRYPOINT [ "/sbin/tini", "--" ]

View File

@@ -403,3 +403,15 @@ func (s *consensus) GetHeadersSelectedTip() (*externalapi.DomainHash, error) {
return s.headersSelectedTipStore.HeadersSelectedTip(s.databaseContext)
}
func (s *consensus) Anticone(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
s.lock.Lock()
defer s.lock.Unlock()
err := s.validateBlockHashExists(blockHash)
if err != nil {
return nil, err
}
return s.dagTraversalManager.Anticone(blockHash)
}

View File

@@ -0,0 +1,6 @@
package consensus
import "github.com/kaspanet/kaspad/domain/consensus/model"
// GHOSTDAGManagerConstructor is the function signature for a constructor of a type implementing model.GHOSTDAGManager
type GHOSTDAGManagerConstructor func(model.DBReader, model.DAGTopologyManager, model.GHOSTDAGDataStore, model.BlockHeaderStore, model.KType) model.GHOSTDAGManager

View File

@@ -1473,100 +1473,6 @@ func (x *DbUtxoDiff) GetToRemove() []*DbUtxoCollectionItem {
return nil
}
type DbPruningPointUTXOSetBytes struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Bytes []byte `protobuf:"bytes,1,opt,name=bytes,proto3" json:"bytes,omitempty"`
}
func (x *DbPruningPointUTXOSetBytes) Reset() {
*x = DbPruningPointUTXOSetBytes{}
if protoimpl.UnsafeEnabled {
mi := &file_dbobjects_proto_msgTypes[24]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DbPruningPointUTXOSetBytes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DbPruningPointUTXOSetBytes) ProtoMessage() {}
func (x *DbPruningPointUTXOSetBytes) ProtoReflect() protoreflect.Message {
mi := &file_dbobjects_proto_msgTypes[24]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DbPruningPointUTXOSetBytes.ProtoReflect.Descriptor instead.
func (*DbPruningPointUTXOSetBytes) Descriptor() ([]byte, []int) {
return file_dbobjects_proto_rawDescGZIP(), []int{24}
}
func (x *DbPruningPointUTXOSetBytes) GetBytes() []byte {
if x != nil {
return x.Bytes
}
return nil
}
type DbHeaderTips struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Tips []*DbHash `protobuf:"bytes,1,rep,name=tips,proto3" json:"tips,omitempty"`
}
func (x *DbHeaderTips) Reset() {
*x = DbHeaderTips{}
if protoimpl.UnsafeEnabled {
mi := &file_dbobjects_proto_msgTypes[25]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DbHeaderTips) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DbHeaderTips) ProtoMessage() {}
func (x *DbHeaderTips) ProtoReflect() protoreflect.Message {
mi := &file_dbobjects_proto_msgTypes[25]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DbHeaderTips.ProtoReflect.Descriptor instead.
func (*DbHeaderTips) Descriptor() ([]byte, []int) {
return file_dbobjects_proto_rawDescGZIP(), []int{25}
}
func (x *DbHeaderTips) GetTips() []*DbHash {
if x != nil {
return x.Tips
}
return nil
}
type DbTips struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -1578,7 +1484,7 @@ type DbTips struct {
func (x *DbTips) Reset() {
*x = DbTips{}
if protoimpl.UnsafeEnabled {
mi := &file_dbobjects_proto_msgTypes[26]
mi := &file_dbobjects_proto_msgTypes[24]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1591,7 +1497,7 @@ func (x *DbTips) String() string {
func (*DbTips) ProtoMessage() {}
func (x *DbTips) ProtoReflect() protoreflect.Message {
mi := &file_dbobjects_proto_msgTypes[26]
mi := &file_dbobjects_proto_msgTypes[24]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1604,7 +1510,7 @@ func (x *DbTips) ProtoReflect() protoreflect.Message {
// Deprecated: Use DbTips.ProtoReflect.Descriptor instead.
func (*DbTips) Descriptor() ([]byte, []int) {
return file_dbobjects_proto_rawDescGZIP(), []int{26}
return file_dbobjects_proto_rawDescGZIP(), []int{24}
}
func (x *DbTips) GetTips() []*DbHash {
@@ -1625,7 +1531,7 @@ type DbVirtualDiffParents struct {
func (x *DbVirtualDiffParents) Reset() {
*x = DbVirtualDiffParents{}
if protoimpl.UnsafeEnabled {
mi := &file_dbobjects_proto_msgTypes[27]
mi := &file_dbobjects_proto_msgTypes[25]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1638,7 +1544,7 @@ func (x *DbVirtualDiffParents) String() string {
func (*DbVirtualDiffParents) ProtoMessage() {}
func (x *DbVirtualDiffParents) ProtoReflect() protoreflect.Message {
mi := &file_dbobjects_proto_msgTypes[27]
mi := &file_dbobjects_proto_msgTypes[25]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1651,7 +1557,7 @@ func (x *DbVirtualDiffParents) ProtoReflect() protoreflect.Message {
// Deprecated: Use DbVirtualDiffParents.ProtoReflect.Descriptor instead.
func (*DbVirtualDiffParents) Descriptor() ([]byte, []int) {
return file_dbobjects_proto_rawDescGZIP(), []int{27}
return file_dbobjects_proto_rawDescGZIP(), []int{25}
}
func (x *DbVirtualDiffParents) GetVirtualDiffParents() []*DbHash {
@@ -1672,7 +1578,7 @@ type DbBlockCount struct {
func (x *DbBlockCount) Reset() {
*x = DbBlockCount{}
if protoimpl.UnsafeEnabled {
mi := &file_dbobjects_proto_msgTypes[28]
mi := &file_dbobjects_proto_msgTypes[26]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1685,7 +1591,7 @@ func (x *DbBlockCount) String() string {
func (*DbBlockCount) ProtoMessage() {}
func (x *DbBlockCount) ProtoReflect() protoreflect.Message {
mi := &file_dbobjects_proto_msgTypes[28]
mi := &file_dbobjects_proto_msgTypes[26]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1698,7 +1604,7 @@ func (x *DbBlockCount) ProtoReflect() protoreflect.Message {
// Deprecated: Use DbBlockCount.ProtoReflect.Descriptor instead.
func (*DbBlockCount) Descriptor() ([]byte, []int) {
return file_dbobjects_proto_rawDescGZIP(), []int{28}
return file_dbobjects_proto_rawDescGZIP(), []int{26}
}
func (x *DbBlockCount) GetCount() uint64 {
@@ -1719,7 +1625,7 @@ type DbBlockHeaderCount struct {
func (x *DbBlockHeaderCount) Reset() {
*x = DbBlockHeaderCount{}
if protoimpl.UnsafeEnabled {
mi := &file_dbobjects_proto_msgTypes[29]
mi := &file_dbobjects_proto_msgTypes[27]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1732,7 +1638,7 @@ func (x *DbBlockHeaderCount) String() string {
func (*DbBlockHeaderCount) ProtoMessage() {}
func (x *DbBlockHeaderCount) ProtoReflect() protoreflect.Message {
mi := &file_dbobjects_proto_msgTypes[29]
mi := &file_dbobjects_proto_msgTypes[27]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1745,7 +1651,7 @@ func (x *DbBlockHeaderCount) ProtoReflect() protoreflect.Message {
// Deprecated: Use DbBlockHeaderCount.ProtoReflect.Descriptor instead.
func (*DbBlockHeaderCount) Descriptor() ([]byte, []int) {
return file_dbobjects_proto_rawDescGZIP(), []int{29}
return file_dbobjects_proto_rawDescGZIP(), []int{27}
}
func (x *DbBlockHeaderCount) GetCount() uint64 {
@@ -1981,32 +1887,25 @@ var file_dbobjects_proto_rawDesc = []byte{
0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x73,
0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x62, 0x55,
0x74, 0x78, 0x6f, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x74, 0x65,
0x6d, 0x52, 0x08, 0x74, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x22, 0x32, 0x0a, 0x1a, 0x44,
0x62, 0x50, 0x72, 0x75, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x55, 0x54, 0x58,
0x4f, 0x53, 0x65, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74,
0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x22,
0x39, 0x0a, 0x0c, 0x44, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x54, 0x69, 0x70, 0x73, 0x12,
0x29, 0x0a, 0x04, 0x74, 0x69, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e,
0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x62,
0x48, 0x61, 0x73, 0x68, 0x52, 0x04, 0x74, 0x69, 0x70, 0x73, 0x22, 0x33, 0x0a, 0x06, 0x44, 0x62,
0x54, 0x69, 0x70, 0x73, 0x12, 0x29, 0x0a, 0x04, 0x74, 0x69, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x15, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x2e, 0x44, 0x62, 0x48, 0x61, 0x73, 0x68, 0x52, 0x04, 0x74, 0x69, 0x70, 0x73, 0x22,
0x5d, 0x0a, 0x14, 0x44, 0x62, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x44, 0x69, 0x66, 0x66,
0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x45, 0x0a, 0x12, 0x76, 0x69, 0x72, 0x74, 0x75,
0x61, 0x6c, 0x44, 0x69, 0x66, 0x66, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20,
0x6d, 0x52, 0x08, 0x74, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x22, 0x33, 0x0a, 0x06, 0x44,
0x62, 0x54, 0x69, 0x70, 0x73, 0x12, 0x29, 0x0a, 0x04, 0x74, 0x69, 0x70, 0x73, 0x18, 0x01, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x62, 0x48, 0x61, 0x73, 0x68, 0x52, 0x12, 0x76, 0x69, 0x72, 0x74,
0x75, 0x61, 0x6c, 0x44, 0x69, 0x66, 0x66, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x24,
0x0a, 0x0c, 0x44, 0x62, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14,
0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63,
0x6f, 0x75, 0x6e, 0x74, 0x22, 0x2a, 0x0a, 0x12, 0x44, 0x62, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48,
0x65, 0x61, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f,
0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74,
0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b,
0x61, 0x73, 0x70, 0x61, 0x6e, 0x65, 0x74, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x64, 0x2f, 0x73,
0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x62, 0x48, 0x61, 0x73, 0x68, 0x52, 0x04, 0x74, 0x69, 0x70, 0x73,
0x22, 0x5d, 0x0a, 0x14, 0x44, 0x62, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x44, 0x69, 0x66,
0x66, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x45, 0x0a, 0x12, 0x76, 0x69, 0x72, 0x74,
0x75, 0x61, 0x6c, 0x44, 0x69, 0x66, 0x66, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x62, 0x48, 0x61, 0x73, 0x68, 0x52, 0x12, 0x76, 0x69, 0x72,
0x74, 0x75, 0x61, 0x6c, 0x44, 0x69, 0x66, 0x66, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x22,
0x24, 0x0a, 0x0c, 0x44, 0x62, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12,
0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05,
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x2a, 0x0a, 0x12, 0x44, 0x62, 0x42, 0x6c, 0x6f, 0x63, 0x6b,
0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63,
0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e,
0x74, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
0x6b, 0x61, 0x73, 0x70, 0x61, 0x6e, 0x65, 0x74, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x64, 0x2f,
0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -2021,7 +1920,7 @@ func file_dbobjects_proto_rawDescGZIP() []byte {
return file_dbobjects_proto_rawDescData
}
var file_dbobjects_proto_msgTypes = make([]protoimpl.MessageInfo, 30)
var file_dbobjects_proto_msgTypes = make([]protoimpl.MessageInfo, 28)
var file_dbobjects_proto_goTypes = []interface{}{
(*DbBlock)(nil), // 0: serialization.DbBlock
(*DbBlockHeader)(nil), // 1: serialization.DbBlockHeader
@@ -2047,12 +1946,10 @@ var file_dbobjects_proto_goTypes = []interface{}{
(*DbReachabilityData)(nil), // 21: serialization.DbReachabilityData
(*DbReachabilityInterval)(nil), // 22: serialization.DbReachabilityInterval
(*DbUtxoDiff)(nil), // 23: serialization.DbUtxoDiff
(*DbPruningPointUTXOSetBytes)(nil), // 24: serialization.DbPruningPointUTXOSetBytes
(*DbHeaderTips)(nil), // 25: serialization.DbHeaderTips
(*DbTips)(nil), // 26: serialization.DbTips
(*DbVirtualDiffParents)(nil), // 27: serialization.DbVirtualDiffParents
(*DbBlockCount)(nil), // 28: serialization.DbBlockCount
(*DbBlockHeaderCount)(nil), // 29: serialization.DbBlockHeaderCount
(*DbTips)(nil), // 24: serialization.DbTips
(*DbVirtualDiffParents)(nil), // 25: serialization.DbVirtualDiffParents
(*DbBlockCount)(nil), // 26: serialization.DbBlockCount
(*DbBlockHeaderCount)(nil), // 27: serialization.DbBlockHeaderCount
}
var file_dbobjects_proto_depIdxs = []int32{
1, // 0: serialization.DbBlock.header:type_name -> serialization.DbBlockHeader
@@ -2090,14 +1987,13 @@ var file_dbobjects_proto_depIdxs = []int32{
2, // 32: serialization.DbReachabilityData.futureCoveringSet:type_name -> serialization.DbHash
18, // 33: serialization.DbUtxoDiff.toAdd:type_name -> serialization.DbUtxoCollectionItem
18, // 34: serialization.DbUtxoDiff.toRemove:type_name -> serialization.DbUtxoCollectionItem
2, // 35: serialization.DbHeaderTips.tips:type_name -> serialization.DbHash
2, // 36: serialization.DbTips.tips:type_name -> serialization.DbHash
2, // 37: serialization.DbVirtualDiffParents.virtualDiffParents:type_name -> serialization.DbHash
38, // [38:38] is the sub-list for method output_type
38, // [38:38] is the sub-list for method input_type
38, // [38:38] is the sub-list for extension type_name
38, // [38:38] is the sub-list for extension extendee
0, // [0:38] is the sub-list for field type_name
2, // 35: serialization.DbTips.tips:type_name -> serialization.DbHash
2, // 36: serialization.DbVirtualDiffParents.virtualDiffParents:type_name -> serialization.DbHash
37, // [37:37] is the sub-list for method output_type
37, // [37:37] is the sub-list for method input_type
37, // [37:37] is the sub-list for extension type_name
37, // [37:37] is the sub-list for extension extendee
0, // [0:37] is the sub-list for field type_name
}
func init() { file_dbobjects_proto_init() }
@@ -2395,30 +2291,6 @@ func file_dbobjects_proto_init() {
}
}
file_dbobjects_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DbPruningPointUTXOSetBytes); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_dbobjects_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DbHeaderTips); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_dbobjects_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DbTips); i {
case 0:
return &v.state
@@ -2430,7 +2302,7 @@ func file_dbobjects_proto_init() {
return nil
}
}
file_dbobjects_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} {
file_dbobjects_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DbVirtualDiffParents); i {
case 0:
return &v.state
@@ -2442,7 +2314,7 @@ func file_dbobjects_proto_init() {
return nil
}
}
file_dbobjects_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} {
file_dbobjects_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DbBlockCount); i {
case 0:
return &v.state
@@ -2454,7 +2326,7 @@ func file_dbobjects_proto_init() {
return nil
}
}
file_dbobjects_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} {
file_dbobjects_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DbBlockHeaderCount); i {
case 0:
return &v.state
@@ -2473,7 +2345,7 @@ func file_dbobjects_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_dbobjects_proto_rawDesc,
NumEnums: 0,
NumMessages: 30,
NumMessages: 28,
NumExtensions: 0,
NumServices: 0,
},

View File

@@ -139,14 +139,6 @@ message DbUtxoDiff {
repeated DbUtxoCollectionItem toRemove = 2;
}
message DbPruningPointUTXOSetBytes {
bytes bytes = 1;
}
message DbHeaderTips {
repeated DbHash tips = 1;
}
message DbTips {
repeated DbHash tips = 1;
}

View File

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

View File

@@ -19,11 +19,11 @@ type acceptanceDataStore struct {
}
// New instantiates a new AcceptanceDataStore
func New(cacheSize int) model.AcceptanceDataStore {
func New(cacheSize int, preallocate bool) model.AcceptanceDataStore {
return &acceptanceDataStore{
staging: make(map[externalapi.DomainHash]externalapi.AcceptanceData),
toDelete: make(map[externalapi.DomainHash]struct{}),
cache: lrucache.New(cacheSize),
cache: lrucache.New(cacheSize, preallocate),
}
}

View File

@@ -21,11 +21,11 @@ type blockHeaderStore struct {
}
// New instantiates a new BlockHeaderStore
func New(dbContext model.DBReader, cacheSize int) (model.BlockHeaderStore, error) {
func New(dbContext model.DBReader, cacheSize int, preallocate bool) (model.BlockHeaderStore, error) {
blockHeaderStore := &blockHeaderStore{
staging: make(map[externalapi.DomainHash]externalapi.BlockHeader),
toDelete: make(map[externalapi.DomainHash]struct{}),
cache: lrucache.New(cacheSize),
cache: lrucache.New(cacheSize, preallocate),
}
err := blockHeaderStore.initializeCount(dbContext)

View File

@@ -18,10 +18,10 @@ type blockRelationStore struct {
}
// New instantiates a new BlockRelationStore
func New(cacheSize int) model.BlockRelationStore {
func New(cacheSize int, preallocate bool) model.BlockRelationStore {
return &blockRelationStore{
staging: make(map[externalapi.DomainHash]*model.BlockRelations),
cache: lrucache.New(cacheSize),
cache: lrucache.New(cacheSize, preallocate),
}
}

View File

@@ -18,10 +18,10 @@ type blockStatusStore struct {
}
// New instantiates a new BlockStatusStore
func New(cacheSize int) model.BlockStatusStore {
func New(cacheSize int, preallocate bool) model.BlockStatusStore {
return &blockStatusStore{
staging: make(map[externalapi.DomainHash]externalapi.BlockStatus),
cache: lrucache.New(cacheSize),
cache: lrucache.New(cacheSize, preallocate),
}
}

View File

@@ -21,11 +21,11 @@ type blockStore struct {
}
// New instantiates a new BlockStore
func New(dbContext model.DBReader, cacheSize int) (model.BlockStore, error) {
func New(dbContext model.DBReader, cacheSize int, preallocate bool) (model.BlockStore, error) {
blockStore := &blockStore{
staging: make(map[externalapi.DomainHash]*externalapi.DomainBlock),
toDelete: make(map[externalapi.DomainHash]struct{}),
cache: lrucache.New(cacheSize),
cache: lrucache.New(cacheSize, preallocate),
}
err := blockStore.initializeCount(dbContext)
@@ -212,3 +212,34 @@ func (bs *blockStore) serializeBlockCount(count uint64) ([]byte, error) {
dbBlockCount := &serialization.DbBlockCount{Count: count}
return proto.Marshal(dbBlockCount)
}
type allBlockHashesIterator struct {
cursor model.DBCursor
}
func (a allBlockHashesIterator) First() bool {
return a.cursor.First()
}
func (a allBlockHashesIterator) Next() bool {
return a.cursor.Next()
}
func (a allBlockHashesIterator) Get() (*externalapi.DomainHash, error) {
key, err := a.cursor.Key()
if err != nil {
return nil, err
}
blockHashBytes := key.Suffix()
return externalapi.NewDomainHashFromByteSlice(blockHashBytes)
}
func (bs *blockStore) AllBlockHashesIterator(dbContext model.DBReader) (model.BlockIterator, error) {
cursor, err := dbContext.Cursor(bucket)
if err != nil {
return nil, err
}
return &allBlockHashesIterator{cursor: cursor}, nil
}

View File

@@ -19,9 +19,9 @@ type consensusStateStore struct {
}
// New instantiates a new ConsensusStateStore
func New(utxoSetCacheSize int) model.ConsensusStateStore {
func New(utxoSetCacheSize int, preallocate bool) model.ConsensusStateStore {
return &consensusStateStore{
virtualUTXOSetCache: utxolrucache.New(utxoSetCacheSize),
virtualUTXOSetCache: utxolrucache.New(utxoSetCacheSize, preallocate),
}
}

View File

@@ -16,11 +16,11 @@ type finalityStore struct {
}
// New instantiates a new FinalityStore
func New(cacheSize int) model.FinalityStore {
func New(cacheSize int, preallocate bool) model.FinalityStore {
return &finalityStore{
staging: make(map[externalapi.DomainHash]*externalapi.DomainHash),
toDelete: make(map[externalapi.DomainHash]struct{}),
cache: lrucache.New(cacheSize),
cache: lrucache.New(cacheSize, preallocate),
}
}

View File

@@ -18,10 +18,10 @@ type ghostdagDataStore struct {
}
// New instantiates a new GHOSTDAGDataStore
func New(cacheSize int) model.GHOSTDAGDataStore {
func New(cacheSize int, preallocate bool) model.GHOSTDAGDataStore {
return &ghostdagDataStore{
staging: make(map[externalapi.DomainHash]*model.BlockGHOSTDAGData),
cache: lrucache.New(cacheSize),
cache: lrucache.New(cacheSize, preallocate),
}
}

View File

@@ -26,14 +26,14 @@ type headersSelectedChainStore struct {
}
// New instantiates a new HeadersSelectedChainStore
func New(cacheSize int) model.HeadersSelectedChainStore {
func New(cacheSize int, preallocate bool) model.HeadersSelectedChainStore {
return &headersSelectedChainStore{
stagingAddedByHash: make(map[externalapi.DomainHash]uint64),
stagingRemovedByHash: make(map[externalapi.DomainHash]struct{}),
stagingAddedByIndex: make(map[uint64]*externalapi.DomainHash),
stagingRemovedByIndex: make(map[uint64]struct{}),
cacheByIndex: lrucacheuint64tohash.New(cacheSize),
cacheByHash: lrucache.New(cacheSize),
cacheByIndex: lrucacheuint64tohash.New(cacheSize, preallocate),
cacheByHash: lrucache.New(cacheSize, preallocate),
}
}

View File

@@ -19,11 +19,11 @@ type multisetStore struct {
}
// New instantiates a new MultisetStore
func New(cacheSize int) model.MultisetStore {
func New(cacheSize int, preallocate bool) model.MultisetStore {
return &multisetStore{
staging: make(map[externalapi.DomainHash]model.Multiset),
toDelete: make(map[externalapi.DomainHash]struct{}),
cache: lrucache.New(cacheSize),
cache: lrucache.New(cacheSize, preallocate),
}
}

View File

@@ -196,20 +196,6 @@ func (ps *pruningStore) deserializePruningPoint(pruningPointBytes []byte) (*exte
return serialization.DbHashToDomainHash(dbHash)
}
func (ps *pruningStore) serializeUTXOSetBytes(pruningPointUTXOSetBytes []byte) ([]byte, error) {
return proto.Marshal(&serialization.DbPruningPointUTXOSetBytes{Bytes: pruningPointUTXOSetBytes})
}
func (ps *pruningStore) deserializeUTXOSetBytes(dbPruningPointUTXOSetBytes []byte) ([]byte, error) {
dbPruningPointUTXOSet := &serialization.DbPruningPointUTXOSetBytes{}
err := proto.Unmarshal(dbPruningPointUTXOSetBytes, dbPruningPointUTXOSet)
if err != nil {
return nil, err
}
return dbPruningPointUTXOSet.Bytes, nil
}
func (ps *pruningStore) HasPruningPoint(dbContext model.DBReader) (bool, error) {
if ps.pruningPointStaging != nil {
return true, nil

View File

@@ -21,10 +21,10 @@ type reachabilityDataStore struct {
}
// New instantiates a new ReachabilityDataStore
func New(cacheSize int) model.ReachabilityDataStore {
func New(cacheSize int, preallocate bool) model.ReachabilityDataStore {
return &reachabilityDataStore{
reachabilityDataStaging: make(map[externalapi.DomainHash]model.ReachabilityData),
reachabilityDataCache: lrucache.New(cacheSize),
reachabilityDataCache: lrucache.New(cacheSize, preallocate),
}
}

View File

@@ -23,13 +23,13 @@ type utxoDiffStore struct {
}
// New instantiates a new UTXODiffStore
func New(cacheSize int) model.UTXODiffStore {
func New(cacheSize int, preallocate bool) model.UTXODiffStore {
return &utxoDiffStore{
utxoDiffStaging: make(map[externalapi.DomainHash]model.UTXODiff),
utxoDiffChildStaging: make(map[externalapi.DomainHash]*externalapi.DomainHash),
toDelete: make(map[externalapi.DomainHash]struct{}),
utxoDiffCache: lrucache.New(cacheSize),
utxoDiffChildCache: lrucache.New(cacheSize),
utxoDiffCache: lrucache.New(cacheSize, preallocate),
utxoDiffChildCache: lrucache.New(cacheSize, preallocate),
}
}

View File

@@ -1,11 +1,12 @@
package consensus
import (
"github.com/kaspanet/kaspad/domain/consensus/datastructures/headersselectedchainstore"
"io/ioutil"
"os"
"sync"
"github.com/kaspanet/kaspad/domain/consensus/datastructures/headersselectedchainstore"
"github.com/kaspanet/kaspad/domain/consensus/processes/dagtraversalmanager"
"github.com/kaspanet/kaspad/domain/consensus/processes/finalitymanager"
@@ -45,21 +46,37 @@ import (
"github.com/kaspanet/kaspad/infrastructure/db/database/ldb"
)
const (
defaultTestLeveldbCacheSizeMiB = 8
defaultPreallocateCaches = true
defaultTestPreallocateCaches = false
)
// Factory instantiates new Consensuses
type Factory interface {
NewConsensus(dagParams *dagconfig.Params, db infrastructuredatabase.Database, isArchivalNode bool) (
externalapi.Consensus, error)
NewTestConsensus(dagParams *dagconfig.Params, isArchivalNode bool, testName string) (
tc testapi.TestConsensus, teardown func(keepDataDir bool), err error)
NewTestConsensusWithDataDir(dagParams *dagconfig.Params, dataDir string, isArchivalNode bool) (
tc testapi.TestConsensus, teardown func(keepDataDir bool), err error)
SetTestDataDir(dataDir string)
SetTestGHOSTDAGManager(ghostdagConstructor GHOSTDAGManagerConstructor)
SetTestLevelDBCacheSize(cacheSizeMiB int)
SetTestPreAllocateCache(preallocateCaches bool)
}
type factory struct{}
type factory struct {
dataDir string
ghostdagConstructor GHOSTDAGManagerConstructor
cacheSizeMiB *int
preallocateCaches *bool
}
// NewFactory creates a new Consensus factory
func NewFactory() Factory {
return &factory{}
return &factory{
ghostdagConstructor: ghostdagmanager.New,
}
}
// NewConsensus instantiates a new Consensus
@@ -70,27 +87,39 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
pruningWindowSizeForCaches := int(dagParams.PruningDepth())
var preallocateCaches bool
if f.preallocateCaches != nil {
preallocateCaches = *f.preallocateCaches
} else {
preallocateCaches = defaultPreallocateCaches
}
// This is used for caches that are used as part of deletePastBlocks that need to traverse until
// the previous pruning point.
pruningWindowSizePlusFinalityDepthForCache := int(dagParams.PruningDepth() + dagParams.FinalityDepth())
// Data Structures
acceptanceDataStore := acceptancedatastore.New(200)
blockStore, err := blockstore.New(dbManager, 200)
acceptanceDataStore := acceptancedatastore.New(200, preallocateCaches)
blockStore, err := blockstore.New(dbManager, 200, preallocateCaches)
if err != nil {
return nil, err
}
blockHeaderStore, err := blockheaderstore.New(dbManager, 10_000)
blockHeaderStore, err := blockheaderstore.New(dbManager, 10_000, preallocateCaches)
if err != nil {
return nil, err
}
blockRelationStore := blockrelationstore.New(pruningWindowSizeForCaches)
blockStatusStore := blockstatusstore.New(200)
multisetStore := multisetstore.New(200)
blockRelationStore := blockrelationstore.New(pruningWindowSizePlusFinalityDepthForCache, preallocateCaches)
blockStatusStore := blockstatusstore.New(pruningWindowSizePlusFinalityDepthForCache, preallocateCaches)
multisetStore := multisetstore.New(200, preallocateCaches)
pruningStore := pruningstore.New()
reachabilityDataStore := reachabilitydatastore.New(pruningWindowSizeForCaches)
utxoDiffStore := utxodiffstore.New(200)
consensusStateStore := consensusstatestore.New(10_000)
ghostdagDataStore := ghostdagdatastore.New(pruningWindowSizeForCaches)
reachabilityDataStore := reachabilitydatastore.New(pruningWindowSizePlusFinalityDepthForCache, preallocateCaches)
utxoDiffStore := utxodiffstore.New(200, preallocateCaches)
consensusStateStore := consensusstatestore.New(10_000, preallocateCaches)
ghostdagDataStore := ghostdagdatastore.New(pruningWindowSizeForCaches, preallocateCaches)
headersSelectedTipStore := headersselectedtipstore.New()
finalityStore := finalitystore.New(200)
headersSelectedChainStore := headersselectedchainstore.New(pruningWindowSizeForCaches)
finalityStore := finalitystore.New(200, preallocateCaches)
headersSelectedChainStore := headersselectedchainstore.New(pruningWindowSizeForCaches, preallocateCaches)
// Processes
reachabilityManager := reachabilitymanager.New(
@@ -102,7 +131,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
reachabilityManager,
blockRelationStore,
ghostdagDataStore)
ghostdagManager := ghostdagmanager.New(
ghostdagManager := f.ghostdagConstructor(
dbManager,
dagTopologyManager,
ghostdagDataStore,
@@ -113,7 +142,8 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
dagTopologyManager,
ghostdagDataStore,
reachabilityDataStore,
ghostdagManager)
ghostdagManager,
consensusStateStore)
pastMedianTimeManager := pastmediantimemanager.New(
dagParams.TimestampDeviationTolerance,
dbManager,
@@ -270,6 +300,8 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
coinbaseManager,
consensusStateManager,
ghostdagManager,
transactionValidator,
acceptanceDataStore,
blockRelationStore,
multisetStore,
@@ -375,19 +407,23 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
func (f *factory) NewTestConsensus(dagParams *dagconfig.Params, isArchivalNode bool, testName string) (
tc testapi.TestConsensus, teardown func(keepDataDir bool), err error) {
dataDir, err := ioutil.TempDir("", testName)
if err != nil {
return nil, nil, err
datadir := f.dataDir
if datadir == "" {
datadir, err = ioutil.TempDir("", testName)
if err != nil {
return nil, nil, err
}
}
return f.NewTestConsensusWithDataDir(dagParams, dataDir, isArchivalNode)
}
func (f *factory) NewTestConsensusWithDataDir(dagParams *dagconfig.Params, dataDir string, isArchivalNode bool) (
tc testapi.TestConsensus, teardown func(keepDataDir bool), err error) {
db, err := ldb.NewLevelDB(dataDir)
var cacheSizeMiB int
if f.cacheSizeMiB != nil {
cacheSizeMiB = *f.cacheSizeMiB
} else {
cacheSizeMiB = defaultTestLeveldbCacheSizeMiB
}
if f.preallocateCaches == nil {
f.SetTestPreAllocateCache(defaultTestPreallocateCaches)
}
db, err := ldb.NewLevelDB(datadir, cacheSizeMiB)
if err != nil {
return nil, nil, err
}
@@ -397,9 +433,7 @@ func (f *factory) NewTestConsensusWithDataDir(dagParams *dagconfig.Params, dataD
}
consensusAsImplementation := consensusAsInterface.(*consensus)
testConsensusStateManager := consensusstatemanager.NewTestConsensusStateManager(consensusAsImplementation.consensusStateManager)
testTransactionValidator := transactionvalidator.NewTestTransactionValidator(consensusAsImplementation.transactionValidator)
tstConsensus := &testConsensus{
@@ -415,12 +449,26 @@ func (f *factory) NewTestConsensusWithDataDir(dagParams *dagconfig.Params, dataD
teardown = func(keepDataDir bool) {
db.Close()
if !keepDataDir {
err := os.RemoveAll(dataDir)
err := os.RemoveAll(f.dataDir)
if err != nil {
log.Errorf("Error removing data directory for test consensus: %s", err)
}
}
}
return tstConsensus, teardown, nil
}
func (f *factory) SetTestDataDir(dataDir string) {
f.dataDir = dataDir
}
func (f *factory) SetTestGHOSTDAGManager(ghostdagConstructor GHOSTDAGManagerConstructor) {
f.ghostdagConstructor = ghostdagConstructor
}
func (f *factory) SetTestLevelDBCacheSize(cacheSizeMiB int) {
f.cacheSizeMiB = &cacheSizeMiB
}
func (f *factory) SetTestPreAllocateCache(preallocateCaches bool) {
f.preallocateCaches = &preallocateCaches
}

View File

@@ -18,7 +18,7 @@ func TestNewConsensus(t *testing.T) {
return
}
db, err := ldb.NewLevelDB(tmpDir)
db, err := ldb.NewLevelDB(tmpDir, 8)
if err != nil {
t.Fatalf("error in NewLevelDB: %s", err)
}

View File

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

View File

@@ -5,6 +5,7 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
// BlockHeap represents a heap of block hashes, providing a priority-queue functionality
type BlockHeap interface {
Push(blockHash *externalapi.DomainHash) error
PushSlice(blockHash []*externalapi.DomainHash) error
Pop() *externalapi.DomainHash
Len() int
ToSlice() []*externalapi.DomainHash

View File

@@ -4,6 +4,7 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
// BlockIterator is an iterator over blocks according to some order.
type BlockIterator interface {
First() bool
Next() bool
Get() *externalapi.DomainHash
Get() (*externalapi.DomainHash, error)
}

View File

@@ -29,4 +29,5 @@ type Consensus interface {
GetVirtualSelectedParentChainFromBlock(blockHash *DomainHash) (*SelectedChainPath, error)
IsInSelectedParentChainOf(blockHashA *DomainHash, blockHashB *DomainHash) (bool, error)
GetHeadersSelectedTip() (*DomainHash, error)
Anticone(blockHash *DomainHash) ([]*DomainHash, error)
}

View File

@@ -85,6 +85,16 @@ func (hash *DomainHash) Equal(other *DomainHash) bool {
return hash.hashArray == other.hashArray
}
// Less returns true if hash is less than other
func (hash *DomainHash) Less(other *DomainHash) bool {
return bytes.Compare(hash.hashArray[:], other.hashArray[:]) < 0
}
// LessOrEqual returns true if hash is smaller or equal to other
func (hash *DomainHash) LessOrEqual(other *DomainHash) bool {
return bytes.Compare(hash.hashArray[:], other.hashArray[:]) <= 0
}
// CloneHashes returns a clone of the given hashes slice.
// Note: since DomainHash is a read-only type, the clone is shallow
func CloneHashes(hashes []*DomainHash) []*DomainHash {
@@ -106,8 +116,3 @@ func HashesEqual(a, b []*DomainHash) bool {
}
return true
}
// Less returns true iff hash a is less than hash b
func Less(a, b *DomainHash) bool {
return bytes.Compare(a.hashArray[:], b.hashArray[:]) < 0
}

View File

@@ -313,6 +313,16 @@ func (id *DomainTransactionID) Equal(other *DomainTransactionID) bool {
return (*DomainHash)(id).Equal((*DomainHash)(other))
}
// Less returns true if id is less than other
func (id *DomainTransactionID) Less(other *DomainTransactionID) bool {
return (*DomainHash)(id).Less((*DomainHash)(other))
}
// LessOrEqual returns true if id is smaller or equal to other
func (id *DomainTransactionID) LessOrEqual(other *DomainTransactionID) bool {
return (*DomainHash)(id).LessOrEqual((*DomainHash)(other))
}
// ByteArray returns the bytes in this transactionID represented as a byte array.
// The transactionID bytes are cloned, therefore it is safe to modify the resulting array.
func (id *DomainTransactionID) ByteArray() *[DomainHashSize]byte {

View File

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

View File

@@ -10,7 +10,6 @@ type DAGTopologyManager interface {
IsParentOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error)
IsChildOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error)
IsAncestorOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error)
IsDescendantOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error)
IsAncestorOfAny(blockHash *externalapi.DomainHash, potentialDescendants []*externalapi.DomainHash) (bool, error)
IsInSelectedParentChainOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error)
ChildInSelectedParentChainOf(context, highHash *externalapi.DomainHash) (*externalapi.DomainHash, error)

View File

@@ -7,9 +7,10 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
type DAGTraversalManager interface {
BlockAtDepth(highHash *externalapi.DomainHash, depth uint64) (*externalapi.DomainHash, error)
LowestChainBlockAboveOrEqualToBlueScore(highHash *externalapi.DomainHash, blueScore uint64) (*externalapi.DomainHash, error)
SelectedParentIterator(highHash *externalapi.DomainHash) BlockIterator
// SelectedChildIterator should return a BlockIterator that iterates
// from lowHash (exclusive) to highHash (inclusive) over highHash's selected parent chain
SelectedChildIterator(highHash, lowHash *externalapi.DomainHash) (BlockIterator, error)
AnticoneFromContext(context, lowHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error)
Anticone(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error)
BlueWindow(highHash *externalapi.DomainHash, windowSize int) ([]*externalapi.DomainHash, error)
NewDownHeap() BlockHeap
NewUpHeap() BlockHeap

View File

@@ -9,4 +9,5 @@ type PruningManager interface {
ClearImportedPruningPointData() error
AppendImportedPruningPointUTXOs(outpointAndUTXOEntryPairs []*externalapi.OutpointAndUTXOEntryPair) error
UpdatePruningPointUTXOSetIfRequired() error
PruneAllBlocksBelow(pruningPointHash *externalapi.DomainHash) error
}

View File

@@ -1,11 +1,12 @@
package testapi
import (
"io"
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/infrastructure/db/database"
"io"
)
// MineJSONBlockType indicates which type of blocks MineJSON mines
@@ -50,6 +51,8 @@ type TestConsensus interface {
MineJSON(r io.Reader, blockType MineJSONBlockType) (tips []*externalapi.DomainHash, err error)
DiscardAllStores()
RenderDAGToDot(filename string) error
AcceptanceDataStore() model.AcceptanceDataStore
BlockHeaderStore() model.BlockHeaderStore
BlockRelationStore() model.BlockRelationStore

View File

@@ -1,7 +1,9 @@
package blockbuilder
import (
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
"github.com/pkg/errors"
"sort"
"github.com/kaspanet/kaspad/domain/consensus/model"
@@ -9,7 +11,6 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"github.com/kaspanet/kaspad/domain/consensus/utils/merkle"
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionid"
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/util/mstime"
)
@@ -22,6 +23,7 @@ type blockBuilder struct {
coinbaseManager model.CoinbaseManager
consensusStateManager model.ConsensusStateManager
ghostdagManager model.GHOSTDAGManager
transactionValidator model.TransactionValidator
acceptanceDataStore model.AcceptanceDataStore
blockRelationStore model.BlockRelationStore
@@ -38,6 +40,7 @@ func New(
coinbaseManager model.CoinbaseManager,
consensusStateManager model.ConsensusStateManager,
ghostdagManager model.GHOSTDAGManager,
transactionValidator model.TransactionValidator,
acceptanceDataStore model.AcceptanceDataStore,
blockRelationStore model.BlockRelationStore,
@@ -52,10 +55,12 @@ func New(
coinbaseManager: coinbaseManager,
consensusStateManager: consensusStateManager,
ghostdagManager: ghostdagManager,
acceptanceDataStore: acceptanceDataStore,
blockRelationStore: blockRelationStore,
multisetStore: multisetStore,
ghostdagDataStore: ghostdagDataStore,
transactionValidator: transactionValidator,
acceptanceDataStore: acceptanceDataStore,
blockRelationStore: blockRelationStore,
multisetStore: multisetStore,
ghostdagDataStore: ghostdagDataStore,
}
}
@@ -73,6 +78,11 @@ func (bb *blockBuilder) BuildBlock(coinbaseData *externalapi.DomainCoinbaseData,
func (bb *blockBuilder) buildBlock(coinbaseData *externalapi.DomainCoinbaseData,
transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlock, error) {
err := bb.validateTransactions(transactions)
if err != nil {
return nil, err
}
coinbase, err := bb.newBlockCoinbaseTransaction(coinbaseData)
if err != nil {
return nil, err
@@ -90,6 +100,53 @@ func (bb *blockBuilder) buildBlock(coinbaseData *externalapi.DomainCoinbaseData,
}, nil
}
func (bb *blockBuilder) validateTransactions(transactions []*externalapi.DomainTransaction) error {
invalidTransactions := make([]ruleerrors.InvalidTransaction, 0)
for _, transaction := range transactions {
err := bb.validateTransaction(transaction)
if err != nil {
if !errors.As(err, &ruleerrors.RuleError{}) {
return err
}
invalidTransactions = append(invalidTransactions,
ruleerrors.InvalidTransaction{Transaction: transaction, Error: err})
}
}
if len(invalidTransactions) > 0 {
return ruleerrors.NewErrInvalidTransactionsInNewBlock(invalidTransactions)
}
return nil
}
func (bb *blockBuilder) validateTransaction(transaction *externalapi.DomainTransaction) error {
originalEntries := make([]externalapi.UTXOEntry, len(transaction.Inputs))
for i, input := range transaction.Inputs {
originalEntries[i] = input.UTXOEntry
input.UTXOEntry = nil
}
defer func() {
for i, input := range transaction.Inputs {
input.UTXOEntry = originalEntries[i]
}
}()
err := bb.consensusStateManager.PopulateTransactionWithUTXOEntries(transaction)
if err != nil {
return err
}
virtualSelectedParentMedianTime, err := bb.pastMedianTimeManager.PastMedianTime(model.VirtualBlockHash)
if err != nil {
return err
}
return bb.transactionValidator.ValidateTransactionInContextAndPopulateMassAndFee(transaction,
model.VirtualBlockHash, virtualSelectedParentMedianTime)
}
func (bb *blockBuilder) newBlockCoinbaseTransaction(
coinbaseData *externalapi.DomainCoinbaseData) (*externalapi.DomainTransaction, error) {
@@ -197,7 +254,7 @@ func (bb *blockBuilder) calculateAcceptedIDMerkleRoot(acceptanceData externalapi
sort.Slice(acceptedTransactions, func(i, j int) bool {
acceptedTransactionIID := consensushashing.TransactionID(acceptedTransactions[i])
acceptedTransactionJID := consensushashing.TransactionID(acceptedTransactions[j])
return transactionid.Less(acceptedTransactionIID, acceptedTransactionJID)
return acceptedTransactionIID.Less(acceptedTransactionJID)
})
return merkle.CalculateIDMerkleRoot(acceptedTransactions), nil

View File

@@ -1,6 +1,8 @@
package blockbuilder_test
import (
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
"testing"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
@@ -22,12 +24,14 @@ func TestBuildBlockErrorCases(t *testing.T) {
}
defer teardown(false)
tests := []struct {
name string
coinbaseData *externalapi.DomainCoinbaseData
transactions []*externalapi.DomainTransaction
expectedErrorType error
}{
type testData struct {
name string
coinbaseData *externalapi.DomainCoinbaseData
transactions []*externalapi.DomainTransaction
testFunc func(test testData, err error) error
}
tests := []testData{
{
"scriptPublicKey too long",
&externalapi.DomainCoinbaseData{
@@ -38,7 +42,78 @@ func TestBuildBlockErrorCases(t *testing.T) {
ExtraData: nil,
},
nil,
ruleerrors.ErrBadCoinbasePayloadLen,
func(_ testData, err error) error {
if !errors.Is(err, ruleerrors.ErrBadCoinbasePayloadLen) {
return errors.Errorf("Unexpected error: %+v", err)
}
return nil
},
},
{
"missing UTXO transactions",
&externalapi.DomainCoinbaseData{
ScriptPublicKey: &externalapi.ScriptPublicKey{
Script: nil,
Version: 0,
},
ExtraData: nil,
},
[]*externalapi.DomainTransaction{
{
Version: constants.MaxTransactionVersion,
Inputs: []*externalapi.DomainTransactionInput{
{
PreviousOutpoint: externalapi.DomainOutpoint{
TransactionID: externalapi.DomainTransactionID{}, Index: 0},
},
},
Outputs: nil,
LockTime: 0,
SubnetworkID: subnetworks.SubnetworkIDNative,
Gas: 0,
PayloadHash: externalapi.DomainHash{},
Payload: []byte{0},
},
{
Version: constants.MaxTransactionVersion,
Inputs: []*externalapi.DomainTransactionInput{
{
PreviousOutpoint: externalapi.DomainOutpoint{
TransactionID: externalapi.DomainTransactionID{}, Index: 0},
},
},
Outputs: nil,
LockTime: 0,
SubnetworkID: subnetworks.SubnetworkIDNative,
Gas: 0,
PayloadHash: externalapi.DomainHash{},
Payload: []byte{1},
},
},
func(test testData, err error) error {
errInvalidTransactionsInNewBlock := ruleerrors.ErrInvalidTransactionsInNewBlock{}
if !errors.As(err, &errInvalidTransactionsInNewBlock) {
return errors.Errorf("Unexpected error: %+v", err)
}
if len(errInvalidTransactionsInNewBlock.InvalidTransactions) != len(test.transactions) {
return errors.Errorf("Expected %d transaction but got %d",
len(test.transactions), len(errInvalidTransactionsInNewBlock.InvalidTransactions))
}
for i, invalidTx := range errInvalidTransactionsInNewBlock.InvalidTransactions {
if !invalidTx.Transaction.Equal(test.transactions[i]) {
return errors.Errorf("Expected transaction %d to be equal to its corresponding "+
"test transaction", i)
}
if !errors.As(invalidTx.Error, &ruleerrors.ErrMissingTxOut{}) {
return errors.Errorf("Unexpected error for transaction %d: %+v", i, invalidTx.Error)
}
}
return nil
},
},
}
@@ -48,8 +123,11 @@ func TestBuildBlockErrorCases(t *testing.T) {
t.Errorf("%s: No error from BuildBlock", test.name)
return
}
if test.expectedErrorType != nil && !errors.Is(err, test.expectedErrorType) {
t.Errorf("%s: Expected error '%s', but got '%s'", test.name, test.expectedErrorType, err)
err := test.testFunc(test, err)
if err != nil {
t.Errorf("%s: %s", test.name, err)
return
}
}
})

View File

@@ -0,0 +1,77 @@
// 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 (
"time"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/util/mstime"
)
// BlockLogger is a type tracking the amount of blocks/headers/transactions to log the time it took to receive them
type BlockLogger struct {
receivedLogBlocks int64
receivedLogHeaders int64
receivedLogTransactions int64
lastBlockLogTime time.Time
}
// NewBlockLogger creates a new instance with zeroed blocks/headers/transactions/time counters.
func NewBlockLogger() *BlockLogger {
return &BlockLogger{
receivedLogBlocks: 0,
receivedLogHeaders: 0,
receivedLogTransactions: 0,
lastBlockLogTime: time.Now(),
}
}
// 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 (bl *BlockLogger) LogBlock(block *externalapi.DomainBlock) {
if len(block.Transactions) == 0 {
bl.receivedLogHeaders++
} else {
bl.receivedLogBlocks++
}
bl.receivedLogTransactions += int64(len(block.Transactions))
now := time.Now()
duration := now.Sub(bl.lastBlockLogTime)
if duration < time.Second*10 {
return
}
// Truncate the duration to 10s of milliseconds.
truncatedDuration := duration.Round(10 * time.Millisecond)
// Log information about new block blue score.
blockStr := "blocks"
if bl.receivedLogBlocks == 1 {
blockStr = "block"
}
txStr := "transactions"
if bl.receivedLogTransactions == 1 {
txStr = "transaction"
}
headerStr := "headers"
if bl.receivedLogBlocks == 1 {
headerStr = "header"
}
log.Infof("Processed %d %s and %d %s in the last %s (%d %s, %s)",
bl.receivedLogBlocks, blockStr, bl.receivedLogHeaders, headerStr, truncatedDuration, bl.receivedLogTransactions,
txStr, mstime.UnixMilliseconds(block.Header.TimeInMilliseconds()))
bl.receivedLogBlocks = 0
bl.receivedLogHeaders = 0
bl.receivedLogTransactions = 0
bl.lastBlockLogTime = now
}

View File

@@ -8,4 +8,4 @@ import (
"github.com/kaspanet/kaspad/infrastructure/logger"
)
var log, _ = logger.Get(logger.SubsystemTags.PROT)
var log, _ = logger.Get(logger.SubsystemTags.BDAG)

View File

@@ -3,6 +3,7 @@ package blockprocessor
import (
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/processes/blockprocessor/blocklogger"
"github.com/kaspanet/kaspad/infrastructure/logger"
"time"
)
@@ -13,6 +14,7 @@ type blockProcessor struct {
genesisHash *externalapi.DomainHash
targetTimePerBlock time.Duration
databaseContext model.DBManager
blockLogger *blocklogger.BlockLogger
consensusStateManager model.ConsensusStateManager
pruningManager model.PruningManager
@@ -49,6 +51,7 @@ func New(
genesisHash *externalapi.DomainHash,
targetTimePerBlock time.Duration,
databaseContext model.DBManager,
consensusStateManager model.ConsensusStateManager,
pruningManager model.PruningManager,
blockValidator model.BlockValidator,
@@ -81,6 +84,7 @@ func New(
genesisHash: genesisHash,
targetTimePerBlock: targetTimePerBlock,
databaseContext: databaseContext,
blockLogger: blocklogger.NewBlockLogger(),
pruningManager: pruningManager,
blockValidator: blockValidator,
dagTopologyManager: dagTopologyManager,

View File

@@ -143,6 +143,8 @@ func (bp *blockProcessor) validateAndInsertBlock(block *externalapi.DomainBlock,
return nil, logClosureErr
}
bp.blockLogger.LogBlock(block)
return &externalapi.BlockInsertionResult{
VirtualSelectedParentChainChanges: selectedParentChainChanges,
}, nil
@@ -290,6 +292,9 @@ func (bp *blockProcessor) discardAllChanges() {
}
func (bp *blockProcessor) commitAllChanges() error {
onEnd := logger.LogAndMeasureExecutionTime(log, "commitAllChanges")
defer onEnd()
dbTx, err := bp.databaseContext.Begin()
if err != nil {
return err

View File

@@ -126,7 +126,8 @@ func TestValidateAndInsertErrors(t *testing.T) {
tipHash, emptyCoinbase, tx1 := initData(params)
// Tests all the error case on the function: "checkBlockStatus"(sub-function in function validateBlock)
blockWithStatusInvalid, err := tc.BuildBlock(&emptyCoinbase, []*externalapi.DomainTransaction{tx1, tx1})
blockWithStatusInvalid, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash},
&emptyCoinbase, []*externalapi.DomainTransaction{tx1, tx1})
if err != nil {
t.Fatalf("AddBlock: %+v", err)
}

View File

@@ -28,6 +28,12 @@ func (bp *blockProcessor) validateAndInsertImportedPruningPoint(newPruningPoint
return err
}
log.Info("Deleting block data for all blocks in blockStore")
err = bp.pruningManager.PruneAllBlocksBelow(newPruningPointHash)
if err != nil {
return err
}
log.Infof("Updating consensus state manager according to the new pruning point %s", newPruningPointHash)
err = bp.consensusStateManager.ImportPruningPoint(newPruningPoint)
if err != nil {

View File

@@ -53,12 +53,19 @@ func (bp *blockProcessor) validateBlock(block *externalapi.DomainBlock, isPrunin
err = bp.validatePostProofOfWork(block, isPruningPoint)
if err != nil {
if errors.As(err, &ruleerrors.RuleError{}) {
// If we got ErrMissingParents the block shouldn't be considered as invalid
// because it could be added later on when its parents are present, and if
// we get ErrBadMerkleRoot we shouldn't mark the block as invalid because
// later on we can get the block with transactions that fits the merkle
// root.
if !errors.As(err, &ruleerrors.ErrMissingParents{}) && !errors.Is(err, ruleerrors.ErrBadMerkleRoot) {
// We mark invalid blocks with status externalapi.StatusInvalid except in the
// case of the following errors:
// ErrMissingParents - If we got ErrMissingParents the block shouldn't be
// considered as invalid because it could be added later on when its
// parents are present.
// ErrBadMerkleRoot - if we get ErrBadMerkleRoot we shouldn't mark the
// block as invalid because later on we can get the block with
// transactions that fits the merkle root.
// ErrPrunedBlock - ErrPrunedBlock is an error that rejects a block body and
// not the block as a whole, so we shouldn't mark it as invalid.
if !errors.As(err, &ruleerrors.ErrMissingParents{}) &&
!errors.Is(err, ruleerrors.ErrBadMerkleRoot) &&
!errors.Is(err, ruleerrors.ErrPrunedBlock) {
// Discard all changes so we save only the block status
bp.discardAllChanges()
hash := consensushashing.BlockHash(block)

View File

@@ -5,6 +5,7 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
"github.com/kaspanet/kaspad/domain/dagconfig"
@@ -58,6 +59,17 @@ func TestCheckBlockIsNotPruned(t *testing.T) {
if !errors.Is(err, ruleerrors.ErrPrunedBlock) {
t.Fatalf("Unexpected error: %+v", err)
}
beforePruningBlockBlockStatus, err := tc.BlockStatusStore().Get(tc.DatabaseContext(),
consensushashing.BlockHash(beforePruningBlock))
if err != nil {
t.Fatalf("BlockStatusStore().Get: %+v", err)
}
// Check that the block still has header only status although it got rejected.
if beforePruningBlockBlockStatus != externalapi.StatusHeaderOnly {
t.Fatalf("Unexpected status %s", beforePruningBlockBlockStatus)
}
})
}

View File

@@ -4,6 +4,7 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/domain/consensus/utils/multiset"
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/pkg/errors"
"github.com/kaspanet/kaspad/domain/consensus/model"
@@ -15,8 +16,10 @@ import (
func (csm *consensusStateManager) CalculatePastUTXOAndAcceptanceData(blockHash *externalapi.DomainHash) (
model.UTXODiff, externalapi.AcceptanceData, model.Multiset, error) {
onEnd := logger.LogAndMeasureExecutionTime(log, "CalculatePastUTXOAndAcceptanceData")
defer onEnd()
log.Debugf("CalculatePastUTXOAndAcceptanceData start for block %s", blockHash)
defer log.Debugf("CalculatePastUTXOAndAcceptanceData end for block %s", blockHash)
if blockHash.Equal(csm.genesisHash) {
log.Debugf("Block %s is the genesis. By definition, "+
@@ -35,6 +38,9 @@ func (csm *consensusStateManager) CalculatePastUTXOAndAcceptanceData(blockHash *
if err != nil {
return nil, nil, nil, err
}
log.Debugf("Restored the past UTXO of block %s with selectedParent %s. "+
"Diff toAdd length: %d, toRemove length: %d", blockHash, blockGHOSTDAGData.SelectedParent(),
selectedParentPastUTXO.ToAdd().Len(), selectedParentPastUTXO.ToRemove().Len())
log.Debugf("Applying blue blocks to the selected parent past UTXO of block %s", blockHash)
acceptanceData, utxoDiff, err := csm.applyMergeSetBlocks(blockHash, selectedParentPastUTXO, blockGHOSTDAGData)
@@ -53,8 +59,10 @@ func (csm *consensusStateManager) CalculatePastUTXOAndAcceptanceData(blockHash *
}
func (csm *consensusStateManager) restorePastUTXO(blockHash *externalapi.DomainHash) (model.MutableUTXODiff, error) {
onEnd := logger.LogAndMeasureExecutionTime(log, "restorePastUTXO")
defer onEnd()
log.Debugf("restorePastUTXO start for block %s", blockHash)
defer log.Debugf("restorePastUTXO end for block %s", blockHash)
var err error
@@ -111,7 +119,7 @@ func (csm *consensusStateManager) applyMergeSetBlocks(blockHash *externalapi.Dom
externalapi.AcceptanceData, model.MutableUTXODiff, error) {
log.Debugf("applyMergeSetBlocks start for block %s", blockHash)
defer log.Tracef("applyMergeSetBlocks end for block %s", blockHash)
defer log.Debugf("applyMergeSetBlocks end for block %s", blockHash)
mergeSetHashes := ghostdagData.MergeSet()
log.Debugf("Merge set for block %s is %v", blockHash, mergeSetHashes)
@@ -263,6 +271,9 @@ func (csm *consensusStateManager) checkTransactionMass(
func (csm *consensusStateManager) RestorePastUTXOSetIterator(blockHash *externalapi.DomainHash) (
model.ReadOnlyUTXOSetIterator, error) {
onEnd := logger.LogAndMeasureExecutionTime(log, "RestorePastUTXOSetIterator")
defer onEnd()
blockStatus, err := csm.resolveBlockStatus(blockHash)
if err != nil {
return nil, err
@@ -276,7 +287,7 @@ func (csm *consensusStateManager) RestorePastUTXOSetIterator(blockHash *external
log.Tracef("RestorePastUTXOSetIterator start for block %s", blockHash)
defer log.Tracef("RestorePastUTXOSetIterator end for block %s", blockHash)
log.Tracef("Calculating UTXO diff for block %s", blockHash)
log.Debugf("Calculating UTXO diff for block %s", blockHash)
blockDiff, err := csm.restorePastUTXO(blockHash)
if err != nil {
return nil, err

View File

@@ -61,7 +61,7 @@ func (csm *consensusStateManager) importPruningPoint(newPruningPoint *externalap
}
log.Debugf("The new pruning point UTXO commitment validation passed")
log.Debugf("Staging the the pruning point as the only DAG tip")
log.Debugf("Staging the pruning point as the only DAG tip")
newTips := []*externalapi.DomainHash{newPruningPointHash}
csm.consensusStateStore.StageTips(newTips)
@@ -86,7 +86,7 @@ func (csm *consensusStateManager) importPruningPoint(newPruningPoint *externalap
return err
}
log.Debugf("Staging the new pruning point")
log.Debugf("Staging the new pruning point %s", newPruningPointHash)
csm.pruningStore.StagePruningPoint(newPruningPointHash)
log.Debugf("Populating the pruning point with UTXO entries")

View File

@@ -1,6 +1,7 @@
package consensusstatemanager
import (
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/pkg/errors"
"github.com/kaspanet/kaspad/domain/consensus/model"
@@ -9,8 +10,10 @@ import (
)
func (csm *consensusStateManager) pickVirtualParents(tips []*externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
onEnd := logger.LogAndMeasureExecutionTime(log, "pickVirtualParents")
defer onEnd()
log.Debugf("pickVirtualParents start for tips len: %d", len(tips))
defer log.Debugf("pickVirtualParents end for tips len: %d", len(tips))
log.Debugf("Pushing all tips into a DownHeap")
candidatesHeap := csm.dagTraversalManager.NewDownHeap()
@@ -84,8 +87,8 @@ func (csm *consensusStateManager) pickVirtualParents(tips []*externalapi.DomainH
func (csm *consensusStateManager) selectVirtualSelectedParent(
candidatesHeap model.BlockHeap) (*externalapi.DomainHash, error) {
log.Tracef("selectVirtualSelectedParent start")
defer log.Tracef("selectVirtualSelectedParent end")
onEnd := logger.LogAndMeasureExecutionTime(log, "selectVirtualSelectedParent")
defer onEnd()
disqualifiedCandidates := hashset.New()
@@ -153,8 +156,8 @@ func (csm *consensusStateManager) selectVirtualSelectedParent(
func (csm *consensusStateManager) mergeSetIncrease(
candidate *externalapi.DomainHash, selectedVirtualParents hashset.HashSet) (uint64, error) {
log.Tracef("mergeSetIncrease start")
defer log.Tracef("mergeSetIncrease end")
onEnd := logger.LogAndMeasureExecutionTime(log, "mergeSetIncrease")
defer onEnd()
visited := hashset.New()
queue := csm.dagTraversalManager.NewDownHeap()
@@ -204,8 +207,10 @@ func (csm *consensusStateManager) mergeSetIncrease(
func (csm *consensusStateManager) boundedMergeBreakingParents(
parents []*externalapi.DomainHash) (hashset.HashSet, error) {
onEnd := logger.LogAndMeasureExecutionTime(log, "boundedMergeBreakingParents")
defer onEnd()
log.Tracef("boundedMergeBreakingParents start for parents: %s", parents)
defer log.Tracef("boundedMergeBreakingParents end for parents: %s", parents)
log.Debug("Temporarily setting virtual to all parents, so that we can run ghostdag on it")
err := csm.dagTopologyManager.SetParents(model.VirtualBlockHash, parents)

View File

@@ -56,7 +56,7 @@ func (csm *consensusStateManager) resolveBlockStatus(blockHash *externalapi.Doma
csm.blockStatusStore.Stage(unverifiedBlockHash, blockStatus)
selectedParentStatus = blockStatus
log.Debugf("Block %s status resolved to `%s`", unverifiedBlockHash, blockStatus)
log.Debugf("Block %s status resolved to `%s`, finished %d/%d of unverified blocks", unverifiedBlockHash, blockStatus, len(unverifiedBlocks)-i, len(unverifiedBlocks))
}
return blockStatus, nil
@@ -122,7 +122,7 @@ func (csm *consensusStateManager) getUnverifiedChainBlocks(
func (csm *consensusStateManager) resolveSingleBlockStatus(blockHash *externalapi.DomainHash) (externalapi.BlockStatus, error) {
log.Debugf("resolveSingleBlockStatus start for block %s", blockHash)
defer log.Tracef("resolveSingleBlockStatus end for block %s", blockHash)
defer log.Debugf("resolveSingleBlockStatus end for block %s", blockHash)
log.Tracef("Calculating pastUTXO and acceptance data and multiset for block %s", blockHash)
pastUTXODiff, acceptanceData, multiset, err := csm.CalculatePastUTXOAndAcceptanceData(blockHash)

View File

@@ -3,13 +3,16 @@ package consensusstatemanager
import (
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/infrastructure/logger"
)
func (csm *consensusStateManager) updateVirtual(newBlockHash *externalapi.DomainHash,
tips []*externalapi.DomainHash) (*externalapi.SelectedChainPath, error) {
onEnd := logger.LogAndMeasureExecutionTime(log, "updateVirtual")
defer onEnd()
log.Debugf("updateVirtual start for block %s", newBlockHash)
defer log.Debugf("updateVirtual end for block %s", newBlockHash)
log.Debugf("Saving a reference to the GHOSTDAG data of the old virtual")
var oldVirtualSelectedParent *externalapi.DomainHash
@@ -44,6 +47,9 @@ func (csm *consensusStateManager) updateVirtual(newBlockHash *externalapi.Domain
if err != nil {
return nil, err
}
log.Debugf("Calculated the past UTXO of the new virtual. "+
"Diff toAdd length: %d, toRemove length: %d",
virtualUTXODiff.ToAdd().Len(), virtualUTXODiff.ToRemove().Len())
log.Debugf("Staging new acceptance data for the virtual block")
csm.acceptanceDataStore.Stage(model.VirtualBlockHash, virtualAcceptanceData)
@@ -73,6 +79,8 @@ func (csm *consensusStateManager) updateVirtual(newBlockHash *externalapi.Domain
if err != nil {
return nil, err
}
log.Debugf("Selected parent chain changes: %d blocks were removed and %d blocks were added",
len(selectedParentChainChanges.Removed), len(selectedParentChainChanges.Added))
}
return selectedParentChainChanges, nil

View File

@@ -5,8 +5,6 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper"
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionid"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/domain/consensus/utils/merkle"
@@ -142,8 +140,7 @@ func calculateAcceptedIDMerkleRoot(multiblockAcceptanceData externalapi.Acceptan
}
}
sort.Slice(acceptedTransactions, func(i, j int) bool {
return transactionid.Less(
consensushashing.TransactionID(acceptedTransactions[i]),
return consensushashing.TransactionID(acceptedTransactions[i]).Less(
consensushashing.TransactionID(acceptedTransactions[j]))
})

View File

@@ -32,8 +32,8 @@ func TestConsensusStateManager_pickVirtualParents(t *testing.T) {
t.Fatalf("Consensus failed building a block: %v", err)
}
blockParents := block.Header.ParentHashes()
sort.Sort(consensus.NewTestGhostDAGSorter(virtualRelations.Parents, tc, t))
sort.Sort(consensus.NewTestGhostDAGSorter(blockParents, tc, t))
sort.Sort(testutils.NewTestGhostDAGSorter(virtualRelations.Parents, tc, t))
sort.Sort(testutils.NewTestGhostDAGSorter(blockParents, tc, t))
if !externalapi.HashesEqual(virtualRelations.Parents, blockParents) {
t.Fatalf("Block relations and BuildBlock return different parents for virtual, %s != %s", virtualRelations.Parents, blockParents)
}
@@ -54,7 +54,7 @@ func TestConsensusStateManager_pickVirtualParents(t *testing.T) {
}
virtualParents := getSortedVirtualParents(tc)
sort.Sort(consensus.NewTestGhostDAGSorter(parents, tc, t))
sort.Sort(testutils.NewTestGhostDAGSorter(parents, tc, t))
// Make sure the first half of the blocks are with highest blueWork
// we use (max+1)/2 because the first "half" is rounded up, so `(dividend + (divisor - 1)) / divisor` = `(max + (2-1))/2` = `(max+1)/2`
@@ -102,7 +102,7 @@ func TestConsensusStateManager_pickVirtualParents(t *testing.T) {
parents = append(parents, block)
}
sort.Sort(consensus.NewTestGhostDAGSorter(parents, tc, t))
sort.Sort(testutils.NewTestGhostDAGSorter(parents, tc, t))
virtualParents = getSortedVirtualParents(tc)
if !externalapi.HashesEqual(virtualParents, parents) {
t.Fatalf("Expected VirtualParents and parents to be equal, instead: %s != %s", virtualParents, parents)

View File

@@ -71,11 +71,6 @@ func (dtm *dagTopologyManager) IsAncestorOf(blockHashA *externalapi.DomainHash,
return dtm.reachabilityManager.IsDAGAncestorOf(blockHashA, blockHashB)
}
// IsDescendantOf returns true if blockHashA is a DAG descendant of blockHashB
func (dtm *dagTopologyManager) IsDescendantOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error) {
return dtm.reachabilityManager.IsDAGAncestorOf(blockHashB, blockHashA)
}
// IsAncestorOfAny returns true if `blockHash` is an ancestor of at least one of `potentialDescendants`
func (dtm *dagTopologyManager) IsAncestorOfAny(blockHash *externalapi.DomainHash, potentialDescendants []*externalapi.DomainHash) (bool, error) {
for _, potentialDescendant := range potentialDescendants {

View File

@@ -5,10 +5,12 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/utils/hashset"
)
// AnticoneFromContext returns blocks in (context.Past ⋂ block.Anticone)
func (dtm *dagTraversalManager) AnticoneFromContext(context, block *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
func (dtm *dagTraversalManager) Anticone(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
anticone := []*externalapi.DomainHash{}
queue := []*externalapi.DomainHash{context}
queue, err := dtm.consensusStateStore.Tips(dtm.databaseContext)
if err != nil {
return nil, err
}
visited := hashset.New()
for len(queue) > 0 {
@@ -21,7 +23,7 @@ func (dtm *dagTraversalManager) AnticoneFromContext(context, block *externalapi.
visited.Add(current)
currentIsAncestorOfBlock, err := dtm.dagTopologyManager.IsAncestorOf(current, block)
currentIsAncestorOfBlock, err := dtm.dagTopologyManager.IsAncestorOf(current, blockHash)
if err != nil {
return nil, err
}
@@ -30,7 +32,7 @@ func (dtm *dagTraversalManager) AnticoneFromContext(context, block *externalapi.
continue
}
blockIsAncestorOfCurrent, err := dtm.dagTopologyManager.IsAncestorOf(block, current)
blockIsAncestorOfCurrent, err := dtm.dagTopologyManager.IsAncestorOf(blockHash, current)
if err != nil {
return nil, err
}

View File

@@ -109,6 +109,16 @@ func (bh *blockHeap) Push(blockHash *externalapi.DomainHash) error {
return nil
}
func (bh *blockHeap) PushSlice(blockHashes []*externalapi.DomainHash) error {
for _, blockHash := range blockHashes {
err := bh.Push(blockHash)
if err != nil {
return err
}
}
return nil
}
// Len returns the length of this heap
func (bh *blockHeap) Len() int {
return bh.impl.Len()

View File

@@ -1,7 +1,6 @@
package dagtraversalmanager
import (
"fmt"
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/pkg/errors"
@@ -16,29 +15,7 @@ type dagTraversalManager struct {
ghostdagManager model.GHOSTDAGManager
ghostdagDataStore model.GHOSTDAGDataStore
reachabilityDataStore model.ReachabilityDataStore
}
// selectedParentIterator implements the `model.BlockIterator` API
type selectedParentIterator struct {
databaseContext model.DBReader
ghostdagDataStore model.GHOSTDAGDataStore
current *externalapi.DomainHash
}
func (spi *selectedParentIterator) Next() bool {
if spi.current == nil {
return false
}
ghostdagData, err := spi.ghostdagDataStore.Get(spi.databaseContext, spi.current)
if err != nil {
panic(fmt.Sprintf("ghostdagDataStore is missing ghostdagData for: %v. '%s' ", spi.current, err))
}
spi.current = ghostdagData.SelectedParent()
return spi.current != nil
}
func (spi *selectedParentIterator) Get() *externalapi.DomainHash {
return spi.current
consensusStateStore model.ConsensusStateStore
}
// New instantiates a new DAGTraversalManager
@@ -47,23 +24,15 @@ func New(
dagTopologyManager model.DAGTopologyManager,
ghostdagDataStore model.GHOSTDAGDataStore,
reachabilityDataStore model.ReachabilityDataStore,
ghostdagManager model.GHOSTDAGManager) model.DAGTraversalManager {
ghostdagManager model.GHOSTDAGManager,
conssensusStateStore model.ConsensusStateStore) model.DAGTraversalManager {
return &dagTraversalManager{
databaseContext: databaseContext,
dagTopologyManager: dagTopologyManager,
ghostdagDataStore: ghostdagDataStore,
reachabilityDataStore: reachabilityDataStore,
ghostdagManager: ghostdagManager,
}
}
// SelectedParentIterator creates an iterator over the selected
// parent chain of the given highHash
func (dtm *dagTraversalManager) SelectedParentIterator(highHash *externalapi.DomainHash) model.BlockIterator {
return &selectedParentIterator{
databaseContext: dtm.databaseContext,
ghostdagDataStore: dtm.ghostdagDataStore,
current: highHash,
consensusStateStore: conssensusStateStore,
}
}

View File

@@ -3,11 +3,200 @@ package dagtraversalmanager_test
import (
"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/testutils"
"github.com/kaspanet/kaspad/domain/dagconfig"
"testing"
)
const commonChainSize = 5
const depth uint64 = 2
//TestBlockAtDepthOnChainDag compares the result of BlockAtDepth to the result of looping over the SelectedChain on a single chain DAG.
func TestBlockAtDepthOnChainDag(t *testing.T) {
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
factory := consensus.NewFactory()
tc, tearDown, err := factory.NewTestConsensus(params, false,
"TestBlockAtDepthOnChainDag")
if err != nil {
t.Fatalf("Failed creating a NewTestConsensus: %s", err)
}
defer tearDown(false)
highHash, err := createAChainDAG(params.GenesisHash, tc)
if err != nil {
t.Fatalf("Failed creating a Chain DAG In BlockAtDepthTEST: %+v", err)
}
currentBlockHash := highHash
currentBlockData, err := tc.GHOSTDAGDataStore().Get(tc.DatabaseContext(), currentBlockHash)
if err != nil {
t.Fatalf("Failed getting GHOSTDAGData for block with hash %s: %+v", currentBlockHash.String(), err)
}
for i := uint64(0); i <= depth; i++ {
if currentBlockData.SelectedParent() == nil {
break
}
currentBlockHash = currentBlockData.SelectedParent()
currentBlockData, err = tc.GHOSTDAGDataStore().Get(tc.DatabaseContext(), currentBlockHash)
if err != nil {
t.Fatalf("Failed getting GHOSTDAGData for block with hash %s: %+v", currentBlockHash.String(), err)
}
}
expectedBlockHash := currentBlockHash
actualBlockHash, err := tc.DAGTraversalManager().BlockAtDepth(highHash, depth)
if err != nil {
t.Fatalf("Failed on BlockAtDepth: %+v", err)
}
if !actualBlockHash.Equal(expectedBlockHash) {
t.Fatalf("Expected block %s but got %s", expectedBlockHash, actualBlockHash)
}
})
}
func createAChainDAG(genesisHash *externalapi.DomainHash, tc testapi.TestConsensus) (*externalapi.DomainHash, error) {
block := genesisHash
var err error
for i := 0; i < commonChainSize; i++ {
block, _, err = tc.AddBlock([]*externalapi.DomainHash{block}, nil, nil)
if err != nil {
return nil, err
}
}
return block, nil
}
// TestBlockAtDepthOnDAGWhereTwoBlocksHaveSameSelectedParent compares the results of BlockAtDepth
// of 2 children that have the same selectedParent.
func TestBlockAtDepthOnDAGWhereTwoBlocksHaveSameSelectedParent(t *testing.T) {
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
factory := consensus.NewFactory()
tc, tearDown, err := factory.NewTestConsensus(params, false,
"TestBlockAtDepthOnDAGWhereTwoBlocksHaveSameSelectedParent")
if err != nil {
t.Fatalf("Failed creating a NewTestConsensus: %s", err)
}
defer tearDown(false)
firstChild, secondChild, err := createADAGTwoChildrenWithSameSelectedParent(params.GenesisHash, tc)
if err != nil {
t.Fatalf("Failed creating a DAG where two blocks have same selected parent: %+v", err)
}
actualBlockHash, err := tc.DAGTraversalManager().BlockAtDepth(firstChild, depth)
if err != nil {
t.Fatalf("Failed at BlockAtDepth: %+v", err)
}
expectedSameHash, err := tc.DAGTraversalManager().BlockAtDepth(secondChild, depth)
if err != nil {
t.Fatalf("Failed in BlockAtDepth: %+v", err)
}
if !actualBlockHash.Equal(expectedSameHash) {
t.Fatalf("Expected block %s but got %s", expectedSameHash, actualBlockHash)
}
})
}
func createADAGTwoChildrenWithSameSelectedParent(genesisHash *externalapi.DomainHash,
tc testapi.TestConsensus) (*externalapi.DomainHash, *externalapi.DomainHash, error) {
block := genesisHash
var err error
for i := 0; i < commonChainSize; i++ {
block, _, err = tc.AddBlock([]*externalapi.DomainHash{block}, nil, nil)
if err != nil {
return nil, nil, err
}
}
firstChild, _, err := tc.AddBlock([]*externalapi.DomainHash{block}, nil, nil)
if err != nil {
return nil, nil, err
}
secondChild, _, err := tc.AddBlock([]*externalapi.DomainHash{block}, nil, nil)
if err != nil {
return nil, nil, err
}
return firstChild, secondChild, nil
}
// TestBlockAtDepthOnDAGWithTwoDifferentChains compares results of BlockAtDepth on two different chains,
// on the same DAG, and validates they merge at the correct point.
func TestBlockAtDepthOnDAGWithTwoDifferentChains(t *testing.T) {
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
factory := consensus.NewFactory()
tc, tearDown, err := factory.NewTestConsensus(params, false,
"TestBlockAtDepthOnDAGWithTwoDifferentChains")
if err != nil {
t.Fatalf("Failed creating a NewTestConsensus: %s", err)
}
defer tearDown(false)
const sizeOfTheFirstChildSubChainDAG = 3
const sizeOfTheSecondChildSubChainDAG = 2
firstChild, secondChild, err := createADAGWithTwoDifferentChains(params.GenesisHash, tc, sizeOfTheFirstChildSubChainDAG,
sizeOfTheSecondChildSubChainDAG)
if err != nil {
t.Fatalf("Failed creating a DAG with two different chains in BlockAtDepthTEST: %+v", err)
}
actualBlockHash, err := tc.DAGTraversalManager().BlockAtDepth(firstChild, sizeOfTheFirstChildSubChainDAG)
if err != nil {
t.Fatalf("Failed in BlockAtDepth: %+v", err)
}
expectedSameHash, err := tc.DAGTraversalManager().BlockAtDepth(secondChild, sizeOfTheSecondChildSubChainDAG)
if err != nil {
t.Fatalf("Failed in BlockAtDepth: %+v", err)
}
if !actualBlockHash.Equal(expectedSameHash) {
t.Fatalf("Expected block %s but got %s", expectedSameHash, actualBlockHash)
}
expectedDiffHash, err := tc.DAGTraversalManager().BlockAtDepth(secondChild, sizeOfTheSecondChildSubChainDAG-1)
if err != nil {
t.Fatalf("Failed in BlockAtDepth: %+v", err)
}
if actualBlockHash.Equal(expectedDiffHash) {
t.Fatalf("Expected to a differente block")
}
})
}
func createADAGWithTwoDifferentChains(genesisHash *externalapi.DomainHash, tc testapi.TestConsensus,
sizeOfTheFirstChildSubChainDAG int, sizeOfTheSecondChildSubChainDAG int) (*externalapi.DomainHash, *externalapi.DomainHash, error) {
block := genesisHash
var err error
for i := 0; i < commonChainSize; i++ {
block, _, err = tc.AddBlock([]*externalapi.DomainHash{block}, nil, nil)
if err != nil {
return nil, nil, err
}
}
firstChainTipHash, _, err := tc.AddBlock([]*externalapi.DomainHash{block}, nil, nil)
if err != nil {
return nil, nil, err
}
secondChainTipHash, _, err := tc.AddBlock([]*externalapi.DomainHash{block}, nil, nil)
if err != nil {
return nil, nil, err
}
for i := 0; i < sizeOfTheFirstChildSubChainDAG; i++ {
firstChainTipHash, _, err = tc.AddBlock([]*externalapi.DomainHash{firstChainTipHash}, nil, nil)
if err != nil {
return nil, nil, err
}
}
for i := 0; i < sizeOfTheSecondChildSubChainDAG; i++ {
secondChainTipHash, _, err = tc.AddBlock([]*externalapi.DomainHash{secondChainTipHash}, nil, nil)
if err != nil {
return nil, nil, err
}
}
return firstChainTipHash, secondChainTipHash, nil
}
func TestLowestChainBlockAboveOrEqualToBlueScore(t *testing.T) {
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
params.FinalityDuration = 10 * params.TargetTimePerBlock
@@ -18,7 +207,6 @@ func TestLowestChainBlockAboveOrEqualToBlueScore(t *testing.T) {
t.Fatalf("NewTestConsensus: %s", err)
}
defer tearDown(false)
checkExpectedBlock := func(highHash *externalapi.DomainHash, blueScore uint64, expected *externalapi.DomainHash) {
blockHash, err := tc.DAGTraversalManager().LowestChainBlockAboveOrEqualToBlueScore(highHash, blueScore)
if err != nil {

View File

@@ -11,20 +11,34 @@ type selectedChildIterator struct {
dagTopologyManager model.DAGTopologyManager
reachabilityDataStore model.ReachabilityDataStore
highHash *externalapi.DomainHash
highHash, lowHash *externalapi.DomainHash
current *externalapi.DomainHash
err error
}
func (s *selectedChildIterator) First() bool {
s.current = s.lowHash
return s.Next()
}
func (s *selectedChildIterator) Next() bool {
if s.err != nil {
return true
}
data, err := s.reachabilityDataStore.ReachabilityData(s.databaseContext, s.current)
if err != nil {
panic(err)
s.current = nil
s.err = err
return true
}
for _, child := range data.Children() {
isChildInSelectedParentChainOfHighHash, err := s.dagTopologyManager.IsInSelectedParentChainOf(child, s.highHash)
if err != nil {
panic(err)
s.current = nil
s.err = err
return true
}
if isChildInSelectedParentChainOfHighHash {
@@ -35,10 +49,12 @@ func (s *selectedChildIterator) Next() bool {
return false
}
func (s *selectedChildIterator) Get() *externalapi.DomainHash {
return s.current
func (s *selectedChildIterator) Get() (*externalapi.DomainHash, error) {
return s.current, s.err
}
// SelectedChildIterator returns a BlockIterator that iterates from lowHash (exclusive) to highHash (inclusive) over
// highHash's selected parent chain
func (dtm *dagTraversalManager) SelectedChildIterator(highHash, lowHash *externalapi.DomainHash) (model.BlockIterator, error) {
isLowHashInSelectedParentChainOfHighHash, err := dtm.dagTopologyManager.IsInSelectedParentChainOf(lowHash, highHash)
if err != nil {
@@ -53,6 +69,7 @@ func (dtm *dagTraversalManager) SelectedChildIterator(highHash, lowHash *externa
dagTopologyManager: dtm.dagTopologyManager,
reachabilityDataStore: dtm.reachabilityDataStore,
highHash: highHash,
lowHash: lowHash,
current: lowHash,
}, nil
}

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