mirror of
https://github.com/kaspanet/kaspad.git
synced 2026-02-21 11:17:05 +00:00
Compare commits
29 Commits
v0.11.0-de
...
readme-upd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c477340b4c | ||
|
|
5806fef35f | ||
|
|
227ef392ba | ||
|
|
f3d76d6565 | ||
|
|
df573bba63 | ||
|
|
2a97b7c9bb | ||
|
|
70900c571b | ||
|
|
7292438e4a | ||
|
|
dced1a9376 | ||
|
|
32e8e539ac | ||
|
|
11103a36d3 | ||
|
|
606b781ca0 | ||
|
|
dbf18d8052 | ||
|
|
2a1b38ce7a | ||
|
|
29c410d123 | ||
|
|
6e6fabf956 | ||
|
|
b04292c97a | ||
|
|
765dd170e4 | ||
|
|
8e362845b3 | ||
|
|
5c1ba9170e | ||
|
|
9d8c555bdf | ||
|
|
a2f574eab8 | ||
|
|
7bed86dc1b | ||
|
|
9b81f5145e | ||
|
|
db36110c2f | ||
|
|
cd8341ef57 | ||
|
|
ad8bdbed21 | ||
|
|
7cdceb6df0 | ||
|
|
cc5248106e |
8
.github/workflows/deploy.yaml
vendored
8
.github/workflows/deploy.yaml
vendored
@@ -1,7 +1,7 @@
|
||||
name: Build and upload assets
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
types: [ published ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
# `-tags netgo,osusergo` means use pure go replacements for "os/user" and "net"
|
||||
# `-s -w` strips the binary to produce smaller size binaries
|
||||
run: |
|
||||
go build -v -ldflags="-s -w -extldflags=-static" -tags netgo,osusergo -o ./bin/ ./...
|
||||
go build -v -ldflags="-s -w -extldflags=-static" -tags netgo,osusergo -o ./bin/ . ./cmd/...
|
||||
archive="bin/kaspad-${{ github.event.release.tag_name }}-linux.zip"
|
||||
asset_name="kaspad-${{ github.event.release.tag_name }}-linux.zip"
|
||||
zip -r "${archive}" ./bin/*
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
go build -v -ldflags="-s -w" -o bin/ ./...
|
||||
go build -v -ldflags="-s -w" -o bin/ . ./cmd/...
|
||||
archive="bin/kaspad-${{ github.event.release.tag_name }}-win64.zip"
|
||||
asset_name="kaspad-${{ github.event.release.tag_name }}-win64.zip"
|
||||
powershell "Compress-Archive bin/* \"${archive}\""
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
- name: Build on MacOS
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
go build -v -ldflags="-s -w" -o ./bin/ ./...
|
||||
go build -v -ldflags="-s -w" -o ./bin/ . ./cmd/...
|
||||
archive="bin/kaspad-${{ github.event.release.tag_name }}-osx.zip"
|
||||
asset_name="kaspad-${{ github.event.release.tag_name }}-osx.zip"
|
||||
zip -r "${archive}" ./bin/*
|
||||
|
||||
2
.github/workflows/race.yaml
vendored
2
.github/workflows/race.yaml
vendored
@@ -46,4 +46,4 @@ jobs:
|
||||
run: |
|
||||
git checkout "${{ env.run_on }}"
|
||||
git status
|
||||
go test -race ./...
|
||||
go test -timeout 20m -race ./...
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
|
||||
Kaspad
|
||||
====
|
||||
Warning: This is pre-alpha software. There's no guarantee anything works.
|
||||
====
|
||||
|
||||
[](https://choosealicense.com/licenses/isc/)
|
||||
[](http://godoc.org/github.com/kaspanet/kaspad)
|
||||
|
||||
Kaspad is the reference full node Kaspa implementation written in Go (golang).
|
||||
|
||||
This project is currently under active development and is in a pre-Alpha state.
|
||||
Some things still don't work and APIs are far from finalized. The code is provided for reference only.
|
||||
This project is currently under active development and is in Beta state.
|
||||
|
||||
## What is kaspa
|
||||
|
||||
|
||||
@@ -20,7 +20,10 @@ import (
|
||||
"github.com/kaspanet/kaspad/version"
|
||||
)
|
||||
|
||||
const leveldbCacheSizeMiB = 256
|
||||
const (
|
||||
leveldbCacheSizeMiB = 256
|
||||
defaultDataDirname = "datadir2"
|
||||
)
|
||||
|
||||
var desiredLimits = &limits.DesiredLimits{
|
||||
FileLimitWant: 2048,
|
||||
@@ -159,7 +162,7 @@ func (app *kaspadApp) main(startedChan chan<- struct{}) error {
|
||||
|
||||
// dbPath returns the path to the block database given a database type.
|
||||
func databasePath(cfg *config.Config) string {
|
||||
return filepath.Join(cfg.AppDir, "data")
|
||||
return filepath.Join(cfg.AppDir, defaultDataDirname)
|
||||
}
|
||||
|
||||
func removeDatabase(cfg *config.Config) error {
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
const (
|
||||
// ProtocolVersion is the latest protocol version this package supports.
|
||||
ProtocolVersion uint32 = 1
|
||||
ProtocolVersion uint32 = 3
|
||||
|
||||
// DefaultServices describes the default services that are supported by
|
||||
// the server.
|
||||
|
||||
@@ -152,6 +152,7 @@ func setupRPC(
|
||||
utxoIndex,
|
||||
shutDownChan,
|
||||
)
|
||||
protocolManager.SetOnVirtualChange(rpcManager.NotifyVirtualChange)
|
||||
protocolManager.SetOnBlockAddedToDAGHandler(rpcManager.NotifyBlockAddedToDAG)
|
||||
protocolManager.SetOnPruningPointUTXOSetOverrideHandler(rpcManager.NotifyPruningPointUTXOSetOverride)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
// DefaultTimeout is the default duration to wait for enqueuing/dequeuing
|
||||
// to/from routes.
|
||||
const DefaultTimeout = 30 * time.Second
|
||||
const DefaultTimeout = 120 * time.Second
|
||||
|
||||
// ErrPeerWithSameIDExists signifies that a peer with the same ID already exist.
|
||||
var ErrPeerWithSameIDExists = errors.New("ready peer with the same ID already exists")
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
// relays newly unorphaned transactions and possibly rebroadcast
|
||||
// manually added transactions when not in IBD.
|
||||
func (f *FlowContext) OnNewBlock(block *externalapi.DomainBlock,
|
||||
blockInsertionResult *externalapi.BlockInsertionResult) error {
|
||||
virtualChangeSet *externalapi.VirtualChangeSet) error {
|
||||
|
||||
hash := consensushashing.BlockHash(block)
|
||||
log.Debugf("OnNewBlock start for block %s", hash)
|
||||
@@ -32,10 +32,10 @@ func (f *FlowContext) OnNewBlock(block *externalapi.DomainBlock,
|
||||
log.Debugf("OnNewBlock: block %s unorphaned %d blocks", hash, len(unorphaningResults))
|
||||
|
||||
newBlocks := []*externalapi.DomainBlock{block}
|
||||
newBlockInsertionResults := []*externalapi.BlockInsertionResult{blockInsertionResult}
|
||||
newVirtualChangeSets := []*externalapi.VirtualChangeSet{virtualChangeSet}
|
||||
for _, unorphaningResult := range unorphaningResults {
|
||||
newBlocks = append(newBlocks, unorphaningResult.block)
|
||||
newBlockInsertionResults = append(newBlockInsertionResults, unorphaningResult.blockInsertionResult)
|
||||
newVirtualChangeSets = append(newVirtualChangeSets, unorphaningResult.virtualChangeSet)
|
||||
}
|
||||
|
||||
allAcceptedTransactions := make([]*externalapi.DomainTransaction, 0)
|
||||
@@ -49,8 +49,8 @@ func (f *FlowContext) OnNewBlock(block *externalapi.DomainBlock,
|
||||
|
||||
if f.onBlockAddedToDAGHandler != nil {
|
||||
log.Debugf("OnNewBlock: calling f.onBlockAddedToDAGHandler for block %s", hash)
|
||||
blockInsertionResult = newBlockInsertionResults[i]
|
||||
err := f.onBlockAddedToDAGHandler(newBlock, blockInsertionResult)
|
||||
virtualChangeSet = newVirtualChangeSets[i]
|
||||
err := f.onBlockAddedToDAGHandler(newBlock, virtualChangeSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -60,6 +60,15 @@ func (f *FlowContext) OnNewBlock(block *externalapi.DomainBlock,
|
||||
return f.broadcastTransactionsAfterBlockAdded(newBlocks, allAcceptedTransactions)
|
||||
}
|
||||
|
||||
// OnVirtualChange calls the handler function whenever the virtual block changes.
|
||||
func (f *FlowContext) OnVirtualChange(virtualChangeSet *externalapi.VirtualChangeSet) error {
|
||||
if f.onVirtualChangeHandler != nil && virtualChangeSet != nil {
|
||||
return f.onVirtualChangeHandler(virtualChangeSet)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnPruningPointUTXOSetOverride calls the handler function whenever the UTXO set
|
||||
// resets due to pruning point change via IBD.
|
||||
func (f *FlowContext) OnPruningPointUTXOSetOverride() error {
|
||||
@@ -110,14 +119,14 @@ func (f *FlowContext) AddBlock(block *externalapi.DomainBlock) error {
|
||||
return protocolerrors.Errorf(false, "cannot add header only block")
|
||||
}
|
||||
|
||||
blockInsertionResult, err := f.Domain().Consensus().ValidateAndInsertBlock(block, true)
|
||||
virtualChangeSet, err := f.Domain().Consensus().ValidateAndInsertBlock(block, true)
|
||||
if err != nil {
|
||||
if errors.As(err, &ruleerrors.RuleError{}) {
|
||||
log.Warnf("Validation failed for block %s: %s", consensushashing.BlockHash(block), err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
err = f.OnNewBlock(block, blockInsertionResult)
|
||||
err = f.OnNewBlock(block, virtualChangeSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -22,7 +22,10 @@ import (
|
||||
|
||||
// OnBlockAddedToDAGHandler is a handler function that's triggered
|
||||
// when a block is added to the DAG
|
||||
type OnBlockAddedToDAGHandler func(block *externalapi.DomainBlock, blockInsertionResult *externalapi.BlockInsertionResult) error
|
||||
type OnBlockAddedToDAGHandler func(block *externalapi.DomainBlock, virtualChangeSet *externalapi.VirtualChangeSet) error
|
||||
|
||||
// OnVirtualChangeHandler is a handler function that's triggered when the virtual changes
|
||||
type OnVirtualChangeHandler func(virtualChangeSet *externalapi.VirtualChangeSet) error
|
||||
|
||||
// OnPruningPointUTXOSetOverrideHandler is a handle function that's triggered whenever the UTXO set
|
||||
// resets due to pruning point change via IBD.
|
||||
@@ -43,6 +46,7 @@ type FlowContext struct {
|
||||
|
||||
timeStarted int64
|
||||
|
||||
onVirtualChangeHandler OnVirtualChangeHandler
|
||||
onBlockAddedToDAGHandler OnBlockAddedToDAGHandler
|
||||
onPruningPointUTXOSetOverrideHandler OnPruningPointUTXOSetOverrideHandler
|
||||
onTransactionAddedToMempoolHandler OnTransactionAddedToMempoolHandler
|
||||
@@ -100,6 +104,11 @@ func (f *FlowContext) ShutdownChan() <-chan struct{} {
|
||||
return f.shutdownChan
|
||||
}
|
||||
|
||||
// SetOnVirtualChangeHandler sets the onVirtualChangeHandler handler
|
||||
func (f *FlowContext) SetOnVirtualChangeHandler(onVirtualChangeHandler OnVirtualChangeHandler) {
|
||||
f.onVirtualChangeHandler = onVirtualChangeHandler
|
||||
}
|
||||
|
||||
// SetOnBlockAddedToDAGHandler sets the onBlockAddedToDAG handler
|
||||
func (f *FlowContext) SetOnBlockAddedToDAGHandler(onBlockAddedToDAGHandler OnBlockAddedToDAGHandler) {
|
||||
f.onBlockAddedToDAGHandler = onBlockAddedToDAGHandler
|
||||
|
||||
@@ -17,8 +17,8 @@ const maxOrphans = 600
|
||||
|
||||
// UnorphaningResult is the result of unorphaning a block
|
||||
type UnorphaningResult struct {
|
||||
block *externalapi.DomainBlock
|
||||
blockInsertionResult *externalapi.BlockInsertionResult
|
||||
block *externalapi.DomainBlock
|
||||
virtualChangeSet *externalapi.VirtualChangeSet
|
||||
}
|
||||
|
||||
// AddOrphan adds the block to the orphan set
|
||||
@@ -90,14 +90,14 @@ func (f *FlowContext) UnorphanBlocks(rootBlock *externalapi.DomainBlock) ([]*Uno
|
||||
}
|
||||
}
|
||||
if canBeUnorphaned {
|
||||
blockInsertionResult, unorphaningSucceeded, err := f.unorphanBlock(orphanHash)
|
||||
virtualChangeSet, unorphaningSucceeded, err := f.unorphanBlock(orphanHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if unorphaningSucceeded {
|
||||
unorphaningResults = append(unorphaningResults, &UnorphaningResult{
|
||||
block: orphanBlock,
|
||||
blockInsertionResult: blockInsertionResult,
|
||||
block: orphanBlock,
|
||||
virtualChangeSet: virtualChangeSet,
|
||||
})
|
||||
processQueue = f.addChildOrphansToProcessQueue(&orphanHash, processQueue)
|
||||
}
|
||||
@@ -143,14 +143,14 @@ func (f *FlowContext) findChildOrphansOfBlock(blockHash *externalapi.DomainHash)
|
||||
return childOrphans
|
||||
}
|
||||
|
||||
func (f *FlowContext) unorphanBlock(orphanHash externalapi.DomainHash) (*externalapi.BlockInsertionResult, bool, error) {
|
||||
func (f *FlowContext) unorphanBlock(orphanHash externalapi.DomainHash) (*externalapi.VirtualChangeSet, bool, error) {
|
||||
orphanBlock, ok := f.orphans[orphanHash]
|
||||
if !ok {
|
||||
return nil, false, errors.Errorf("attempted to unorphan a non-orphan block %s", orphanHash)
|
||||
}
|
||||
delete(f.orphans, orphanHash)
|
||||
|
||||
blockInsertionResult, err := f.domain.Consensus().ValidateAndInsertBlock(orphanBlock, true)
|
||||
virtualChangeSet, err := f.domain.Consensus().ValidateAndInsertBlock(orphanBlock, true)
|
||||
if err != nil {
|
||||
if errors.As(err, &ruleerrors.RuleError{}) {
|
||||
log.Warnf("Validation failed for orphan block %s: %s", orphanHash, err)
|
||||
@@ -160,7 +160,7 @@ func (f *FlowContext) unorphanBlock(orphanHash externalapi.DomainHash) (*externa
|
||||
}
|
||||
|
||||
log.Infof("Unorphaned block %s", orphanHash)
|
||||
return blockInsertionResult, true, nil
|
||||
return virtualChangeSet, true, nil
|
||||
}
|
||||
|
||||
// GetOrphanRoots returns the roots of the missing ancestors DAG of the given orphan
|
||||
|
||||
@@ -25,6 +25,10 @@ func (f *FlowContext) ShouldMine() (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if virtualSelectedParent.Equal(f.Config().NetParams().GenesisHash) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
virtualSelectedParentHeader, err := f.domain.Consensus().GetBlockHeader(virtualSelectedParent)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
||||
@@ -3,8 +3,12 @@ package blockrelay
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// PruningPointAndItsAnticoneRequestsContext is the interface for the context needed for the HandlePruningPointAndItsAnticoneRequests flow.
|
||||
@@ -12,51 +16,80 @@ type PruningPointAndItsAnticoneRequestsContext interface {
|
||||
Domain() domain.Domain
|
||||
}
|
||||
|
||||
var isBusy uint32
|
||||
|
||||
// HandlePruningPointAndItsAnticoneRequests listens to appmessage.MsgRequestPruningPointAndItsAnticone messages and sends
|
||||
// the pruning point and its anticone to the requesting peer.
|
||||
func HandlePruningPointAndItsAnticoneRequests(context PruningPointAndItsAnticoneRequestsContext, incomingRoute *router.Route,
|
||||
outgoingRoute *router.Route, peer *peerpkg.Peer) error {
|
||||
|
||||
for {
|
||||
_, err := incomingRoute.Dequeue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("Got request for pruning point and its anticone from %s", peer)
|
||||
|
||||
pruningPointHeaders, err := context.Domain().Consensus().PruningPointHeaders()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgPruningPointHeaders := make([]*appmessage.MsgBlockHeader, len(pruningPointHeaders))
|
||||
for i, header := range pruningPointHeaders {
|
||||
msgPruningPointHeaders[i] = appmessage.DomainBlockHeaderToBlockHeader(header)
|
||||
}
|
||||
|
||||
err = outgoingRoute.Enqueue(appmessage.NewMsgPruningPoints(msgPruningPointHeaders))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blocks, err := context.Domain().Consensus().PruningPointAndItsAnticoneWithTrustedData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, block := range blocks {
|
||||
err = outgoingRoute.Enqueue(appmessage.DomainBlockWithTrustedDataToBlockWithTrustedData(block))
|
||||
err := func() error {
|
||||
_, err := incomingRoute.Dequeue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = outgoingRoute.Enqueue(appmessage.NewMsgDoneBlocksWithTrustedData())
|
||||
if !atomic.CompareAndSwapUint32(&isBusy, 0, 1) {
|
||||
return protocolerrors.Errorf(false, "node is busy with other pruning point anticone requests")
|
||||
}
|
||||
defer atomic.StoreUint32(&isBusy, 0)
|
||||
|
||||
log.Debugf("Got request for pruning point and its anticone from %s", peer)
|
||||
|
||||
pruningPointHeaders, err := context.Domain().Consensus().PruningPointHeaders()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgPruningPointHeaders := make([]*appmessage.MsgBlockHeader, len(pruningPointHeaders))
|
||||
for i, header := range pruningPointHeaders {
|
||||
msgPruningPointHeaders[i] = appmessage.DomainBlockHeaderToBlockHeader(header)
|
||||
}
|
||||
|
||||
err = outgoingRoute.Enqueue(appmessage.NewMsgPruningPoints(msgPruningPointHeaders))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pointAndItsAnticone, err := context.Domain().Consensus().PruningPointAndItsAnticone()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, blockHash := range pointAndItsAnticone {
|
||||
err := sendBlockWithTrustedData(context, outgoingRoute, blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = outgoingRoute.Enqueue(appmessage.NewMsgDoneBlocksWithTrustedData())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("Sent pruning point and its anticone to %s", peer)
|
||||
return nil
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("Sent pruning point and its anticone to %s", peer)
|
||||
}
|
||||
}
|
||||
|
||||
func sendBlockWithTrustedData(context PruningPointAndItsAnticoneRequestsContext, outgoingRoute *router.Route, blockHash *externalapi.DomainHash) error {
|
||||
blockWithTrustedData, err := context.Domain().Consensus().BlockWithTrustedData(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = outgoingRoute.Enqueue(appmessage.DomainBlockWithTrustedDataToBlockWithTrustedData(blockWithTrustedData))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
runtime.GC()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@ var orphanResolutionRange uint32 = 5
|
||||
type RelayInvsContext interface {
|
||||
Domain() domain.Domain
|
||||
Config() *config.Config
|
||||
OnNewBlock(block *externalapi.DomainBlock, blockInsertionResult *externalapi.BlockInsertionResult) error
|
||||
OnNewBlock(block *externalapi.DomainBlock, virtualChangeSet *externalapi.VirtualChangeSet) error
|
||||
OnVirtualChange(virtualChangeSet *externalapi.VirtualChangeSet) error
|
||||
OnPruningPointUTXOSetOverride() error
|
||||
SharedRequestedBlocks() *SharedRequestedBlocks
|
||||
Broadcast(message appmessage.Message) error
|
||||
@@ -81,7 +82,18 @@ func (flow *handleRelayInvsFlow) start() error {
|
||||
continue
|
||||
}
|
||||
|
||||
isGenesisVirtualSelectedParent, err := flow.isGenesisVirtualSelectedParent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if flow.IsOrphan(inv.Hash) {
|
||||
if flow.Config().NetParams().DisallowDirectBlocksOnTopOfGenesis && !flow.Config().AllowSubmitBlockWhenNotSynced && isGenesisVirtualSelectedParent {
|
||||
log.Infof("Cannot process orphan %s for a node with only the genesis block. The node needs to IBD "+
|
||||
"to the recent pruning point before normal operation can resume.", inv.Hash)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debugf("Block %s is a known orphan. Requesting its missing ancestors", inv.Hash)
|
||||
err := flow.AddOrphanRootsToQueue(inv.Hash)
|
||||
if err != nil {
|
||||
@@ -111,8 +123,13 @@ func (flow *handleRelayInvsFlow) start() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if flow.Config().NetParams().DisallowDirectBlocksOnTopOfGenesis && !flow.Config().AllowSubmitBlockWhenNotSynced && !flow.Config().Devnet && flow.isChildOfGenesis(block) {
|
||||
log.Infof("Cannot process %s because it's a direct child of genesis.", consensushashing.BlockHash(block))
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debugf("Processing block %s", inv.Hash)
|
||||
missingParents, blockInsertionResult, err := flow.processBlock(block)
|
||||
missingParents, virtualChangeSet, err := flow.processBlock(block)
|
||||
if err != nil {
|
||||
if errors.Is(err, ruleerrors.ErrPrunedBlock) {
|
||||
log.Infof("Ignoring pruned block %s", inv.Hash)
|
||||
@@ -140,7 +157,7 @@ func (flow *handleRelayInvsFlow) start() error {
|
||||
return err
|
||||
}
|
||||
log.Infof("Accepted block %s via relay", inv.Hash)
|
||||
err = flow.OnNewBlock(block, blockInsertionResult)
|
||||
err = flow.OnNewBlock(block, virtualChangeSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -227,9 +244,9 @@ func (flow *handleRelayInvsFlow) readMsgBlock() (msgBlock *appmessage.MsgBlock,
|
||||
}
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) processBlock(block *externalapi.DomainBlock) ([]*externalapi.DomainHash, *externalapi.BlockInsertionResult, error) {
|
||||
func (flow *handleRelayInvsFlow) processBlock(block *externalapi.DomainBlock) ([]*externalapi.DomainHash, *externalapi.VirtualChangeSet, error) {
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
blockInsertionResult, err := flow.Domain().Consensus().ValidateAndInsertBlock(block, true)
|
||||
virtualChangeSet, err := flow.Domain().Consensus().ValidateAndInsertBlock(block, true)
|
||||
if err != nil {
|
||||
if !errors.As(err, &ruleerrors.RuleError{}) {
|
||||
return nil, nil, errors.Wrapf(err, "failed to process block %s", blockHash)
|
||||
@@ -242,7 +259,7 @@ func (flow *handleRelayInvsFlow) processBlock(block *externalapi.DomainBlock) ([
|
||||
log.Warnf("Rejected block %s from %s: %s", blockHash, flow.peer, err)
|
||||
return nil, nil, protocolerrors.Wrapf(true, err, "got invalid block %s from relay", blockHash)
|
||||
}
|
||||
return nil, blockInsertionResult, nil
|
||||
return nil, virtualChangeSet, nil
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) relayBlock(block *externalapi.DomainBlock) error {
|
||||
@@ -265,6 +282,19 @@ func (flow *handleRelayInvsFlow) processOrphan(block *externalapi.DomainBlock) e
|
||||
return err
|
||||
}
|
||||
if isBlockInOrphanResolutionRange {
|
||||
if flow.Config().NetParams().DisallowDirectBlocksOnTopOfGenesis && !flow.Config().AllowSubmitBlockWhenNotSynced {
|
||||
isGenesisVirtualSelectedParent, err := flow.isGenesisVirtualSelectedParent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isGenesisVirtualSelectedParent {
|
||||
log.Infof("Cannot process orphan %s for a node with only the genesis block. The node needs to IBD "+
|
||||
"to the recent pruning point before normal operation can resume.", blockHash)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("Block %s is within orphan resolution range. "+
|
||||
"Adding it to the orphan set", blockHash)
|
||||
flow.AddOrphan(block)
|
||||
@@ -278,6 +308,20 @@ func (flow *handleRelayInvsFlow) processOrphan(block *externalapi.DomainBlock) e
|
||||
return flow.runIBDIfNotRunning(block)
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) isGenesisVirtualSelectedParent() (bool, error) {
|
||||
virtualSelectedParent, err := flow.Domain().Consensus().GetVirtualSelectedParent()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return virtualSelectedParent.Equal(flow.Config().NetParams().GenesisHash), nil
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) isChildOfGenesis(block *externalapi.DomainBlock) bool {
|
||||
parents := block.Header.DirectParents()
|
||||
return len(parents) == 1 && parents[0].Equal(flow.Config().NetParams().GenesisHash)
|
||||
}
|
||||
|
||||
// isBlockInOrphanResolutionRange finds out whether the given blockHash should be
|
||||
// retrieved via the unorphaning mechanism or via IBD. This method sends a
|
||||
// getBlockLocator request to the peer with a limit of orphanResolutionRange.
|
||||
|
||||
@@ -70,7 +70,8 @@ func (flow *handleRequestPruningPointUTXOSetFlow) waitForRequestPruningPointUTXO
|
||||
}
|
||||
msgRequestPruningPointUTXOSet, ok := message.(*appmessage.MsgRequestPruningPointUTXOSet)
|
||||
if !ok {
|
||||
return nil, protocolerrors.Errorf(true, "received unexpected message type. "+
|
||||
// TODO: Change to shouldBan: true once we fix the bug of getting redundant messages
|
||||
return nil, protocolerrors.Errorf(false, "received unexpected message type. "+
|
||||
"expected: %s, got: %s", appmessage.CmdRequestPruningPointUTXOSet, message.Command())
|
||||
}
|
||||
return msgRequestPruningPointUTXOSet, nil
|
||||
@@ -102,14 +103,17 @@ func (flow *handleRequestPruningPointUTXOSetFlow) sendPruningPointUTXOSet(
|
||||
return err
|
||||
}
|
||||
|
||||
if len(pruningPointUTXOs) < step && chunksSent%ibdBatchSize == 0 {
|
||||
finished := len(pruningPointUTXOs) < step
|
||||
if finished && chunksSent%ibdBatchSize != 0 {
|
||||
log.Debugf("Finished sending UTXOs for pruning block %s",
|
||||
msgRequestPruningPointUTXOSet.PruningPointHash)
|
||||
|
||||
return flow.outgoingRoute.Enqueue(appmessage.NewMsgDonePruningPointUTXOSetChunks())
|
||||
}
|
||||
|
||||
fromOutpoint = pruningPointUTXOs[len(pruningPointUTXOs)-1].Outpoint
|
||||
if len(pruningPointUTXOs) > 0 {
|
||||
fromOutpoint = pruningPointUTXOs[len(pruningPointUTXOs)-1].Outpoint
|
||||
}
|
||||
chunksSent++
|
||||
|
||||
// Wait for the peer to request more chunks every `ibdBatchSize` chunks
|
||||
@@ -120,9 +124,17 @@ func (flow *handleRequestPruningPointUTXOSetFlow) sendPruningPointUTXOSet(
|
||||
}
|
||||
_, ok := message.(*appmessage.MsgRequestNextPruningPointUTXOSetChunk)
|
||||
if !ok {
|
||||
return protocolerrors.Errorf(true, "received unexpected message type. "+
|
||||
// TODO: Change to shouldBan: true once we fix the bug of getting redundant messages
|
||||
return protocolerrors.Errorf(false, "received unexpected message type. "+
|
||||
"expected: %s, got: %s", appmessage.CmdRequestNextPruningPointUTXOSetChunk, message.Command())
|
||||
}
|
||||
|
||||
if finished {
|
||||
log.Debugf("Finished sending UTXOs for pruning block %s",
|
||||
msgRequestPruningPointUTXOSet.PruningPointHash)
|
||||
|
||||
return flow.outgoingRoute.Enqueue(appmessage.NewMsgDonePruningPointUTXOSetChunks())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,19 @@ func (flow *handleRelayInvsFlow) runIBDIfNotRunning(block *externalapi.DomainBlo
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if flow.Config().NetParams().DisallowDirectBlocksOnTopOfGenesis && !flow.Config().AllowSubmitBlockWhenNotSynced {
|
||||
isGenesisVirtualSelectedParent, err := flow.isGenesisVirtualSelectedParent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isGenesisVirtualSelectedParent {
|
||||
log.Infof("Cannot IBD to %s because it won't change the pruning point. The node needs to IBD "+
|
||||
"to the recent pruning point before normal operation can resume.", highHash)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
err = flow.syncPruningPointFutureHeaders(flow.Domain().Consensus(), highestSharedBlockHash, highHash)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -458,7 +471,7 @@ func (flow *handleRelayInvsFlow) syncMissingBlockBodies(highHash *externalapi.Do
|
||||
return err
|
||||
}
|
||||
|
||||
blockInsertionResult, err := flow.Domain().Consensus().ValidateAndInsertBlock(block, false)
|
||||
virtualChangeSet, err := flow.Domain().Consensus().ValidateAndInsertBlock(block, false)
|
||||
if err != nil {
|
||||
if errors.Is(err, ruleerrors.ErrDuplicateBlock) {
|
||||
log.Debugf("Skipping IBD Block %s as it has already been added to the DAG", blockHash)
|
||||
@@ -466,14 +479,36 @@ func (flow *handleRelayInvsFlow) syncMissingBlockBodies(highHash *externalapi.Do
|
||||
}
|
||||
return protocolerrors.ConvertToBanningProtocolErrorIfRuleError(err, "invalid block %s", blockHash)
|
||||
}
|
||||
err = flow.OnNewBlock(block, blockInsertionResult)
|
||||
err = flow.OnNewBlock(block, virtualChangeSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return flow.Domain().Consensus().ResolveVirtual()
|
||||
return flow.resolveVirtual()
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) resolveVirtual() error {
|
||||
for i := 0; ; i++ {
|
||||
if i%10 == 0 {
|
||||
log.Infof("Resolving virtual. This may take some time...")
|
||||
}
|
||||
virtualChangeSet, isCompletelyResolved, err := flow.Domain().Consensus().ResolveVirtual()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = flow.OnVirtualChange(virtualChangeSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isCompletelyResolved {
|
||||
log.Infof("Resolved virtual")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dequeueIncomingMessageAndSkipInvs is a convenience method to be used during
|
||||
|
||||
@@ -130,7 +130,7 @@ func (flow *handleRelayInvsFlow) downloadHeadersAndPruningUTXOSet(highHash *exte
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("Headers downloaded from peer %s", flow.peer)
|
||||
log.Infof("Headers downloaded from peer %s", flow.peer)
|
||||
|
||||
highHashInfo, err := flow.Domain().StagingConsensus().GetBlockInfo(highHash)
|
||||
if err != nil {
|
||||
|
||||
@@ -84,6 +84,11 @@ func (m *Manager) runFlows(flows []*flow, peer *peerpkg.Peer, errChan <-chan err
|
||||
return <-errChan
|
||||
}
|
||||
|
||||
// SetOnVirtualChange sets the onVirtualChangeHandler handler
|
||||
func (m *Manager) SetOnVirtualChange(onVirtualChangeHandler flowcontext.OnVirtualChangeHandler) {
|
||||
m.context.SetOnVirtualChangeHandler(onVirtualChangeHandler)
|
||||
}
|
||||
|
||||
// SetOnBlockAddedToDAGHandler sets the onBlockAddedToDAG handler
|
||||
func (m *Manager) SetOnBlockAddedToDAGHandler(onBlockAddedToDAGHandler flowcontext.OnBlockAddedToDAGHandler) {
|
||||
m.context.SetOnBlockAddedToDAGHandler(onBlockAddedToDAGHandler)
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
// maxProtocolVersion version is the maximum supported protocol
|
||||
// version this kaspad node supports
|
||||
const maxProtocolVersion = 1
|
||||
const maxProtocolVersion = 3
|
||||
|
||||
// Peer holds data about a peer.
|
||||
type Peer struct {
|
||||
|
||||
@@ -102,7 +102,7 @@ func (m *Manager) routerInitializer(router *routerpkg.Router, netConnection *net
|
||||
|
||||
func (m *Manager) handleError(err error, netConnection *netadapter.NetConnection, outgoingRoute *routerpkg.Route) {
|
||||
if protocolErr := (protocolerrors.ProtocolError{}); errors.As(err, &protocolErr) {
|
||||
if !m.context.Config().DisableBanning && protocolErr.ShouldBan {
|
||||
if m.context.Config().EnableBanning && protocolErr.ShouldBan {
|
||||
log.Warnf("Banning %s (reason: %s)", netConnection, protocolErr.Cause)
|
||||
|
||||
err := m.context.ConnectionManager().Ban(netConnection)
|
||||
|
||||
@@ -48,12 +48,31 @@ func NewManager(
|
||||
}
|
||||
|
||||
// NotifyBlockAddedToDAG notifies the manager that a block has been added to the DAG
|
||||
func (m *Manager) NotifyBlockAddedToDAG(block *externalapi.DomainBlock, blockInsertionResult *externalapi.BlockInsertionResult) error {
|
||||
func (m *Manager) NotifyBlockAddedToDAG(block *externalapi.DomainBlock, virtualChangeSet *externalapi.VirtualChangeSet) error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyBlockAddedToDAG")
|
||||
defer onEnd()
|
||||
|
||||
err := m.NotifyVirtualChange(virtualChangeSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rpcBlock := appmessage.DomainBlockToRPCBlock(block)
|
||||
err = m.context.PopulateBlockWithVerboseData(rpcBlock, block.Header, block, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
blockAddedNotification := appmessage.NewBlockAddedNotificationMessage(rpcBlock)
|
||||
return m.context.NotificationManager.NotifyBlockAdded(blockAddedNotification)
|
||||
}
|
||||
|
||||
// NotifyVirtualChange notifies the manager that the virtual block has been changed.
|
||||
func (m *Manager) NotifyVirtualChange(virtualChangeSet *externalapi.VirtualChangeSet) error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyBlockAddedToDAG")
|
||||
defer onEnd()
|
||||
|
||||
if m.context.Config.UTXOIndex {
|
||||
err := m.notifyUTXOsChanged(blockInsertionResult)
|
||||
err := m.notifyUTXOsChanged(virtualChangeSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -69,18 +88,12 @@ func (m *Manager) NotifyBlockAddedToDAG(block *externalapi.DomainBlock, blockIns
|
||||
return err
|
||||
}
|
||||
|
||||
err = m.notifyVirtualSelectedParentChainChanged(blockInsertionResult)
|
||||
err = m.notifyVirtualSelectedParentChainChanged(virtualChangeSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rpcBlock := appmessage.DomainBlockToRPCBlock(block)
|
||||
err = m.context.PopulateBlockWithVerboseData(rpcBlock, block.Header, block, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
blockAddedNotification := appmessage.NewBlockAddedNotificationMessage(rpcBlock)
|
||||
return m.context.NotificationManager.NotifyBlockAdded(blockAddedNotification)
|
||||
return nil
|
||||
}
|
||||
|
||||
// NotifyPruningPointUTXOSetOverride notifies the manager whenever the UTXO index
|
||||
@@ -117,11 +130,11 @@ func (m *Manager) NotifyFinalityConflictResolved(finalityBlockHash string) error
|
||||
return m.context.NotificationManager.NotifyFinalityConflictResolved(notification)
|
||||
}
|
||||
|
||||
func (m *Manager) notifyUTXOsChanged(blockInsertionResult *externalapi.BlockInsertionResult) error {
|
||||
func (m *Manager) notifyUTXOsChanged(virtualChangeSet *externalapi.VirtualChangeSet) error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyUTXOsChanged")
|
||||
defer onEnd()
|
||||
|
||||
utxoIndexChanges, err := m.context.UTXOIndex.Update(blockInsertionResult)
|
||||
utxoIndexChanges, err := m.context.UTXOIndex.Update(virtualChangeSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -171,12 +184,12 @@ func (m *Manager) notifyVirtualDaaScoreChanged() error {
|
||||
return m.context.NotificationManager.NotifyVirtualDaaScoreChanged(notification)
|
||||
}
|
||||
|
||||
func (m *Manager) notifyVirtualSelectedParentChainChanged(blockInsertionResult *externalapi.BlockInsertionResult) error {
|
||||
func (m *Manager) notifyVirtualSelectedParentChainChanged(virtualChangeSet *externalapi.VirtualChangeSet) error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyVirtualSelectedParentChainChanged")
|
||||
defer onEnd()
|
||||
|
||||
notification, err := m.context.ConvertVirtualSelectedParentChainChangesToChainChangedNotificationMessage(
|
||||
blockInsertionResult.VirtualSelectedParentChainChanges)
|
||||
virtualChangeSet.VirtualSelectedParentChainChanges)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -14,9 +14,14 @@ import (
|
||||
func HandleSubmitBlock(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||
submitBlockRequest := request.(*appmessage.SubmitBlockRequestMessage)
|
||||
|
||||
if context.ProtocolManager.IsIBDRunning() {
|
||||
isSynced, err := context.ProtocolManager.ShouldMine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !context.Config.AllowSubmitBlockWhenNotSynced && !isSynced {
|
||||
return &appmessage.SubmitBlockResponseMessage{
|
||||
Error: appmessage.RPCErrorf("Block not submitted - IBD is running"),
|
||||
Error: appmessage.RPCErrorf("Block not submitted - node is not synced"),
|
||||
RejectReason: appmessage.RejectReasonIsInIBD,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -15,13 +15,11 @@ golint -set_exit_status ./...
|
||||
|
||||
staticcheck -checks SA4006,SA4008,SA4009,SA4010,SA5003,SA1004,SA1014,SA1021,SA1023,SA1024,SA1025,SA1026,SA1027,SA1028,SA2000,SA2001,SA2003,SA4000,SA4001,SA4003,SA4004,SA4011,SA4012,SA4013,SA4014,SA4015,SA4016,SA4017,SA4018,SA4019,SA4020,SA4021,SA4022,SA4023,SA5000,SA5002,SA5004,SA5005,SA5007,SA5008,SA5009,SA5010,SA5011,SA5012,SA6001,SA6002,SA9001,SA9002,SA9003,SA9004,SA9005,SA9006,ST1019 ./...
|
||||
|
||||
go vet -composites=false $FLAGS ./...
|
||||
|
||||
go build $FLAGS -o kaspad .
|
||||
|
||||
if [ -n "${NO_PARALLEL}" ]
|
||||
then
|
||||
go test -parallel=1 $FLAGS ./...
|
||||
go test -timeout 20m -parallel=1 $FLAGS ./...
|
||||
else
|
||||
go test $FLAGS ./...
|
||||
go test -timeout 20m $FLAGS ./...
|
||||
fi
|
||||
@@ -1,3 +1,31 @@
|
||||
Kaspad v0.11.7 - 2021-12-11
|
||||
===========================
|
||||
Breaking changes:
|
||||
* kaspawallet: show-address →new-address + show-addresses (#1870)
|
||||
|
||||
Bug fixes:
|
||||
* Fix numThreads using getAEAD instead of decryptMnemonic (#1859)
|
||||
* Apply ResolveVirtual diffs to the UTXO index (#1868)
|
||||
|
||||
Non-breaking changes:
|
||||
* Ignore header mass in devnet and testnet (#1879)
|
||||
* Remove unused args from CalcSubsidy (#1877)
|
||||
* ExpectedHeaderPruningPoint fix (#1876)
|
||||
* Changes to libkaspawallet to support Kaspaper (#1878)
|
||||
* Get rid of genesis's UTXO dump (#1867)
|
||||
|
||||
Kaspad v0.11.2 - 2021-11-11
|
||||
===========================
|
||||
Bug fixes:
|
||||
* Enlarge p2p max message size to 1gb
|
||||
* Fix UTXO chunks logic
|
||||
* Increase tests timeout to 20 minutes
|
||||
|
||||
Kaspad v0.11.1 - 2021-11-09
|
||||
===========================
|
||||
Non-breaking changes:
|
||||
* Cache the miner state
|
||||
|
||||
Kaspad v0.10.2 - 2021-05-18
|
||||
===========================
|
||||
Non-breaking changes:
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/pow"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -143,20 +142,20 @@ func mineNextBlock(mineWhenNotSynced bool) *externalapi.DomainBlock {
|
||||
// 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)
|
||||
block, state := getBlockForMining(mineWhenNotSynced)
|
||||
state.Nonce = nonce
|
||||
atomic.AddUint64(&hashesTried, 1)
|
||||
if pow.CheckProofOfWorkWithTarget(headerForMining, targetDifficulty) {
|
||||
block.Header = headerForMining.ToImmutable()
|
||||
if state.CheckProofOfWork() {
|
||||
mutHeader := block.Header.ToMutable()
|
||||
mutHeader.SetNonce(nonce)
|
||||
block.Header = mutHeader.ToImmutable()
|
||||
log.Infof("Found block %s with parents %s", consensushashing.BlockHash(block), block.Header.DirectParents())
|
||||
return block
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getBlockForMining(mineWhenNotSynced bool) *externalapi.DomainBlock {
|
||||
func getBlockForMining(mineWhenNotSynced bool) (*externalapi.DomainBlock, *pow.State) {
|
||||
tryCount := 0
|
||||
|
||||
const sleepTime = 500 * time.Millisecond
|
||||
@@ -166,7 +165,7 @@ func getBlockForMining(mineWhenNotSynced bool) *externalapi.DomainBlock {
|
||||
tryCount++
|
||||
|
||||
shouldLog := (tryCount-1)%10 == 0
|
||||
template, isSynced := templatemanager.Get()
|
||||
template, state, isSynced := templatemanager.Get()
|
||||
if template == nil {
|
||||
if shouldLog {
|
||||
log.Info("Waiting for the initial template")
|
||||
@@ -182,7 +181,7 @@ func getBlockForMining(mineWhenNotSynced bool) *externalapi.DomainBlock {
|
||||
continue
|
||||
}
|
||||
|
||||
return template
|
||||
return template, state
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,23 +3,26 @@ package templatemanager
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/pow"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var currentTemplate *externalapi.DomainBlock
|
||||
var currentState *pow.State
|
||||
var isSynced bool
|
||||
var lock = &sync.Mutex{}
|
||||
|
||||
// Get returns the template to work on
|
||||
func Get() (*externalapi.DomainBlock, bool) {
|
||||
func Get() (*externalapi.DomainBlock, *pow.State, bool) {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
// Shallow copy the block so when the user replaces the header it won't affect the template here.
|
||||
if currentTemplate == nil {
|
||||
return nil, false
|
||||
return nil, nil, false
|
||||
}
|
||||
block := *currentTemplate
|
||||
return &block, isSynced
|
||||
state := *currentState
|
||||
return &block, &state, isSynced
|
||||
}
|
||||
|
||||
// Set sets the current template to work on
|
||||
@@ -31,6 +34,7 @@ func Set(template *appmessage.GetBlockTemplateResponseMessage) error {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
currentTemplate = block
|
||||
currentState = pow.NewState(block.Header.ToMutable())
|
||||
isSynced = template.IsSynced
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ const (
|
||||
createUnsignedTransactionSubCmd = "create-unsigned-transaction"
|
||||
signSubCmd = "sign"
|
||||
broadcastSubCmd = "broadcast"
|
||||
showAddressSubCmd = "show-address"
|
||||
showAddressesSubCmd = "show-addresses"
|
||||
newAddressSubCmd = "new-address"
|
||||
dumpUnencryptedDataSubCmd = "dump-unencrypted-data"
|
||||
startDaemonSubCmd = "start-daemon"
|
||||
)
|
||||
@@ -75,7 +76,12 @@ type broadcastConfig struct {
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type showAddressConfig struct {
|
||||
type showAddressesConfig struct {
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to (default: localhost:8082)"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type newAddressConfig struct {
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to (default: localhost:8082)"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
@@ -123,9 +129,13 @@ func parseCommandLine() (subCommand string, config interface{}) {
|
||||
parser.AddCommand(broadcastSubCmd, "Broadcast the given transaction",
|
||||
"Broadcast the given transaction", broadcastConf)
|
||||
|
||||
showAddressConf := &showAddressConfig{DaemonAddress: defaultListen}
|
||||
parser.AddCommand(showAddressSubCmd, "Shows the public address of the current wallet",
|
||||
"Shows the public address of the current wallet", showAddressConf)
|
||||
showAddressesConf := &showAddressesConfig{DaemonAddress: defaultListen}
|
||||
parser.AddCommand(showAddressesSubCmd, "Shows all generated public addresses of the current wallet",
|
||||
"Shows all generated public addresses of the current wallet", showAddressesConf)
|
||||
|
||||
newAddressConf := &newAddressConfig{DaemonAddress: defaultListen}
|
||||
parser.AddCommand(newAddressSubCmd, "Generates new public address of the current wallet and shows it",
|
||||
"Generates new public address of the current wallet and shows it", newAddressConf)
|
||||
|
||||
dumpUnencryptedDataConf := &dumpUnencryptedDataConfig{}
|
||||
parser.AddCommand(dumpUnencryptedDataSubCmd, "Prints the unencrypted wallet data",
|
||||
@@ -193,13 +203,20 @@ func parseCommandLine() (subCommand string, config interface{}) {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
config = broadcastConf
|
||||
case showAddressSubCmd:
|
||||
combineNetworkFlags(&showAddressConf.NetworkFlags, &cfg.NetworkFlags)
|
||||
err := showAddressConf.ResolveNetwork(parser)
|
||||
case showAddressesSubCmd:
|
||||
combineNetworkFlags(&showAddressesConf.NetworkFlags, &cfg.NetworkFlags)
|
||||
err := showAddressesConf.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
config = showAddressConf
|
||||
config = showAddressesConf
|
||||
case newAddressSubCmd:
|
||||
combineNetworkFlags(&newAddressConf.NetworkFlags, &cfg.NetworkFlags)
|
||||
err := newAddressConf.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
config = newAddressConf
|
||||
case dumpUnencryptedDataSubCmd:
|
||||
combineNetworkFlags(&dumpUnencryptedDataConf.NetworkFlags, &cfg.NetworkFlags)
|
||||
err := dumpUnencryptedDataConf.ResolveNetwork(parser)
|
||||
|
||||
@@ -56,6 +56,7 @@ func create(conf *createConfig) error {
|
||||
}
|
||||
|
||||
file := keys.File{
|
||||
Version: keys.LastVersion,
|
||||
EncryptedMnemonics: encryptedMnemonics,
|
||||
ExtendedPublicKeys: extendedPublicKeys,
|
||||
MinimumSignatures: conf.MinimumSignatures,
|
||||
|
||||
@@ -19,7 +19,7 @@ func Connect(address string) (pb.KaspawalletdClient, func(), error) {
|
||||
conn, err := grpc.DialContext(ctx, address, grpc.WithInsecure(), grpc.WithBlock())
|
||||
if err != nil {
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
return nil, nil, errors.New("kaspactl daemon is not running, start it with `kaspactl start-daemon`")
|
||||
return nil, nil, errors.New("kaspawallet daemon is not running, start it with `kaspawallet start-daemon`")
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.25.0
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.12.3
|
||||
// source: kaspawalletd.proto
|
||||
|
||||
package pb
|
||||
|
||||
import (
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
@@ -21,10 +20,6 @@ const (
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||
// of the legacy proto package is being used.
|
||||
const _ = proto.ProtoPackageIsVersion4
|
||||
|
||||
type GetBalanceRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -220,14 +215,14 @@ func (x *CreateUnsignedTransactionResponse) GetUnsignedTransaction() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
type GetReceiveAddressRequest struct {
|
||||
type ShowAddressesRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *GetReceiveAddressRequest) Reset() {
|
||||
*x = GetReceiveAddressRequest{}
|
||||
func (x *ShowAddressesRequest) Reset() {
|
||||
*x = ShowAddressesRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_kaspawalletd_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
@@ -235,13 +230,13 @@ func (x *GetReceiveAddressRequest) Reset() {
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GetReceiveAddressRequest) String() string {
|
||||
func (x *ShowAddressesRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetReceiveAddressRequest) ProtoMessage() {}
|
||||
func (*ShowAddressesRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetReceiveAddressRequest) ProtoReflect() protoreflect.Message {
|
||||
func (x *ShowAddressesRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_kaspawalletd_proto_msgTypes[4]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
@@ -253,21 +248,21 @@ func (x *GetReceiveAddressRequest) ProtoReflect() protoreflect.Message {
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetReceiveAddressRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetReceiveAddressRequest) Descriptor() ([]byte, []int) {
|
||||
// Deprecated: Use ShowAddressesRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ShowAddressesRequest) Descriptor() ([]byte, []int) {
|
||||
return file_kaspawalletd_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
type GetReceiveAddressResponse struct {
|
||||
type ShowAddressesResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
|
||||
Address []string `protobuf:"bytes,1,rep,name=address,proto3" json:"address,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GetReceiveAddressResponse) Reset() {
|
||||
*x = GetReceiveAddressResponse{}
|
||||
func (x *ShowAddressesResponse) Reset() {
|
||||
*x = ShowAddressesResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_kaspawalletd_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
@@ -275,13 +270,13 @@ func (x *GetReceiveAddressResponse) Reset() {
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GetReceiveAddressResponse) String() string {
|
||||
func (x *ShowAddressesResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetReceiveAddressResponse) ProtoMessage() {}
|
||||
func (*ShowAddressesResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GetReceiveAddressResponse) ProtoReflect() protoreflect.Message {
|
||||
func (x *ShowAddressesResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_kaspawalletd_proto_msgTypes[5]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
@@ -293,12 +288,97 @@ func (x *GetReceiveAddressResponse) ProtoReflect() protoreflect.Message {
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetReceiveAddressResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GetReceiveAddressResponse) Descriptor() ([]byte, []int) {
|
||||
// Deprecated: Use ShowAddressesResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ShowAddressesResponse) Descriptor() ([]byte, []int) {
|
||||
return file_kaspawalletd_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *GetReceiveAddressResponse) GetAddress() string {
|
||||
func (x *ShowAddressesResponse) GetAddress() []string {
|
||||
if x != nil {
|
||||
return x.Address
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NewAddressRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *NewAddressRequest) Reset() {
|
||||
*x = NewAddressRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_kaspawalletd_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *NewAddressRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*NewAddressRequest) ProtoMessage() {}
|
||||
|
||||
func (x *NewAddressRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_kaspawalletd_proto_msgTypes[6]
|
||||
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 NewAddressRequest.ProtoReflect.Descriptor instead.
|
||||
func (*NewAddressRequest) Descriptor() ([]byte, []int) {
|
||||
return file_kaspawalletd_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
type NewAddressResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
|
||||
}
|
||||
|
||||
func (x *NewAddressResponse) Reset() {
|
||||
*x = NewAddressResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_kaspawalletd_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *NewAddressResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*NewAddressResponse) ProtoMessage() {}
|
||||
|
||||
func (x *NewAddressResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_kaspawalletd_proto_msgTypes[7]
|
||||
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 NewAddressResponse.ProtoReflect.Descriptor instead.
|
||||
func (*NewAddressResponse) Descriptor() ([]byte, []int) {
|
||||
return file_kaspawalletd_proto_rawDescGZIP(), []int{7}
|
||||
}
|
||||
|
||||
func (x *NewAddressResponse) GetAddress() string {
|
||||
if x != nil {
|
||||
return x.Address
|
||||
}
|
||||
@@ -316,7 +396,7 @@ type BroadcastRequest struct {
|
||||
func (x *BroadcastRequest) Reset() {
|
||||
*x = BroadcastRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_kaspawalletd_proto_msgTypes[6]
|
||||
mi := &file_kaspawalletd_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -329,7 +409,7 @@ func (x *BroadcastRequest) String() string {
|
||||
func (*BroadcastRequest) ProtoMessage() {}
|
||||
|
||||
func (x *BroadcastRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_kaspawalletd_proto_msgTypes[6]
|
||||
mi := &file_kaspawalletd_proto_msgTypes[8]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -342,7 +422,7 @@ func (x *BroadcastRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use BroadcastRequest.ProtoReflect.Descriptor instead.
|
||||
func (*BroadcastRequest) Descriptor() ([]byte, []int) {
|
||||
return file_kaspawalletd_proto_rawDescGZIP(), []int{6}
|
||||
return file_kaspawalletd_proto_rawDescGZIP(), []int{8}
|
||||
}
|
||||
|
||||
func (x *BroadcastRequest) GetTransaction() []byte {
|
||||
@@ -363,7 +443,7 @@ type BroadcastResponse struct {
|
||||
func (x *BroadcastResponse) Reset() {
|
||||
*x = BroadcastResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_kaspawalletd_proto_msgTypes[7]
|
||||
mi := &file_kaspawalletd_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -376,7 +456,7 @@ func (x *BroadcastResponse) String() string {
|
||||
func (*BroadcastResponse) ProtoMessage() {}
|
||||
|
||||
func (x *BroadcastResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_kaspawalletd_proto_msgTypes[7]
|
||||
mi := &file_kaspawalletd_proto_msgTypes[9]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -389,7 +469,7 @@ func (x *BroadcastResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use BroadcastResponse.ProtoReflect.Descriptor instead.
|
||||
func (*BroadcastResponse) Descriptor() ([]byte, []int) {
|
||||
return file_kaspawalletd_proto_rawDescGZIP(), []int{7}
|
||||
return file_kaspawalletd_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
func (x *BroadcastResponse) GetTxID() string {
|
||||
@@ -408,7 +488,7 @@ type ShutdownRequest struct {
|
||||
func (x *ShutdownRequest) Reset() {
|
||||
*x = ShutdownRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_kaspawalletd_proto_msgTypes[8]
|
||||
mi := &file_kaspawalletd_proto_msgTypes[10]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -421,7 +501,7 @@ func (x *ShutdownRequest) String() string {
|
||||
func (*ShutdownRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ShutdownRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_kaspawalletd_proto_msgTypes[8]
|
||||
mi := &file_kaspawalletd_proto_msgTypes[10]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -434,7 +514,7 @@ func (x *ShutdownRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ShutdownRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ShutdownRequest) Descriptor() ([]byte, []int) {
|
||||
return file_kaspawalletd_proto_rawDescGZIP(), []int{8}
|
||||
return file_kaspawalletd_proto_rawDescGZIP(), []int{10}
|
||||
}
|
||||
|
||||
type ShutdownResponse struct {
|
||||
@@ -446,7 +526,7 @@ type ShutdownResponse struct {
|
||||
func (x *ShutdownResponse) Reset() {
|
||||
*x = ShutdownResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_kaspawalletd_proto_msgTypes[9]
|
||||
mi := &file_kaspawalletd_proto_msgTypes[11]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -459,7 +539,7 @@ func (x *ShutdownResponse) String() string {
|
||||
func (*ShutdownResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ShutdownResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_kaspawalletd_proto_msgTypes[9]
|
||||
mi := &file_kaspawalletd_proto_msgTypes[11]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -472,7 +552,7 @@ func (x *ShutdownResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ShutdownResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ShutdownResponse) Descriptor() ([]byte, []int) {
|
||||
return file_kaspawalletd_proto_rawDescGZIP(), []int{9}
|
||||
return file_kaspawalletd_proto_rawDescGZIP(), []int{11}
|
||||
}
|
||||
|
||||
var File_kaspawalletd_proto protoreflect.FileDescriptor
|
||||
@@ -496,47 +576,53 @@ var file_kaspawalletd_proto_rawDesc = []byte{
|
||||
0x73, 0x65, 0x12, 0x30, 0x0a, 0x13, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72,
|
||||
0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52,
|
||||
0x13, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x22, 0x1a, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69,
|
||||
0x76, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x22, 0x35, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x41, 0x64,
|
||||
0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a,
|
||||
0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
|
||||
0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x34, 0x0a, 0x10, 0x42, 0x72, 0x6f, 0x61, 0x64,
|
||||
0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x74,
|
||||
0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
|
||||
0x52, 0x0b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x27, 0x0a,
|
||||
0x11, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x04, 0x74, 0x78, 0x49, 0x44, 0x22, 0x11, 0x0a, 0x0f, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f,
|
||||
0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x12, 0x0a, 0x10, 0x53, 0x68, 0x75,
|
||||
0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xe4, 0x02,
|
||||
0x0a, 0x0c, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x12, 0x37,
|
||||
0x0a, 0x0a, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x2e, 0x47,
|
||||
0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x13, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x64, 0x0a, 0x19, 0x43, 0x72, 0x65, 0x61, 0x74,
|
||||
0x65, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x73,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x22, 0x16, 0x0a, 0x14, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72,
|
||||
0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x31, 0x0a, 0x15,
|
||||
0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
|
||||
0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22,
|
||||
0x13, 0x0a, 0x11, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x22, 0x2e, 0x0a, 0x12, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65,
|
||||
0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64,
|
||||
0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64,
|
||||
0x72, 0x65, 0x73, 0x73, 0x22, 0x34, 0x0a, 0x10, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73,
|
||||
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x72, 0x61, 0x6e,
|
||||
0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x74,
|
||||
0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x27, 0x0a, 0x11, 0x42, 0x72,
|
||||
0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||
0x12, 0x0a, 0x04, 0x74, 0x78, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74,
|
||||
0x78, 0x49, 0x44, 0x22, 0x11, 0x0a, 0x0f, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x12, 0x0a, 0x10, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f,
|
||||
0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x91, 0x03, 0x0a, 0x0c, 0x6b,
|
||||
0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x12, 0x37, 0x0a, 0x0a, 0x47,
|
||||
0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x2e, 0x47, 0x65, 0x74, 0x42,
|
||||
0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e,
|
||||
0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x22, 0x00, 0x12, 0x64, 0x0a, 0x19, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x6e,
|
||||
0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x12, 0x21, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e,
|
||||
0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x73,
|
||||
0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
|
||||
0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4c, 0x0a,
|
||||
0x11, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65,
|
||||
0x73, 0x73, 0x12, 0x19, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x41,
|
||||
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e,
|
||||
0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
|
||||
0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x08, 0x53,
|
||||
0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x10, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f,
|
||||
0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x53, 0x68, 0x75, 0x74,
|
||||
0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x34,
|
||||
0x0a, 0x09, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x12, 0x11, 0x2e, 0x42, 0x72,
|
||||
0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12,
|
||||
0x2e, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x22, 0x00, 0x42, 0x36, 0x5a, 0x34, 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, 0x63, 0x6d, 0x64, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c,
|
||||
0x65, 0x74, 0x2f, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x33,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x40, 0x0a, 0x0d, 0x53, 0x68,
|
||||
0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x15, 0x2e, 0x53, 0x68,
|
||||
0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x1a, 0x16, 0x2e, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
|
||||
0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x37, 0x0a, 0x0a,
|
||||
0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x2e, 0x4e, 0x65, 0x77,
|
||||
0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13,
|
||||
0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x08, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77,
|
||||
0x6e, 0x12, 0x10, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x34, 0x0a, 0x09, 0x42, 0x72, 0x6f, 0x61,
|
||||
0x64, 0x63, 0x61, 0x73, 0x74, 0x12, 0x11, 0x2e, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73,
|
||||
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x42, 0x72, 0x6f, 0x61, 0x64,
|
||||
0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x36,
|
||||
0x5a, 0x34, 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, 0x63, 0x6d, 0x64,
|
||||
0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2f, 0x64, 0x61, 0x65,
|
||||
0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -551,35 +637,39 @@ func file_kaspawalletd_proto_rawDescGZIP() []byte {
|
||||
return file_kaspawalletd_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_kaspawalletd_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
|
||||
var file_kaspawalletd_proto_msgTypes = make([]protoimpl.MessageInfo, 12)
|
||||
var file_kaspawalletd_proto_goTypes = []interface{}{
|
||||
(*GetBalanceRequest)(nil), // 0: GetBalanceRequest
|
||||
(*GetBalanceResponse)(nil), // 1: GetBalanceResponse
|
||||
(*CreateUnsignedTransactionRequest)(nil), // 2: CreateUnsignedTransactionRequest
|
||||
(*CreateUnsignedTransactionResponse)(nil), // 3: CreateUnsignedTransactionResponse
|
||||
(*GetReceiveAddressRequest)(nil), // 4: GetReceiveAddressRequest
|
||||
(*GetReceiveAddressResponse)(nil), // 5: GetReceiveAddressResponse
|
||||
(*BroadcastRequest)(nil), // 6: BroadcastRequest
|
||||
(*BroadcastResponse)(nil), // 7: BroadcastResponse
|
||||
(*ShutdownRequest)(nil), // 8: ShutdownRequest
|
||||
(*ShutdownResponse)(nil), // 9: ShutdownResponse
|
||||
(*ShowAddressesRequest)(nil), // 4: ShowAddressesRequest
|
||||
(*ShowAddressesResponse)(nil), // 5: ShowAddressesResponse
|
||||
(*NewAddressRequest)(nil), // 6: NewAddressRequest
|
||||
(*NewAddressResponse)(nil), // 7: NewAddressResponse
|
||||
(*BroadcastRequest)(nil), // 8: BroadcastRequest
|
||||
(*BroadcastResponse)(nil), // 9: BroadcastResponse
|
||||
(*ShutdownRequest)(nil), // 10: ShutdownRequest
|
||||
(*ShutdownResponse)(nil), // 11: ShutdownResponse
|
||||
}
|
||||
var file_kaspawalletd_proto_depIdxs = []int32{
|
||||
0, // 0: kaspawalletd.GetBalance:input_type -> GetBalanceRequest
|
||||
2, // 1: kaspawalletd.CreateUnsignedTransaction:input_type -> CreateUnsignedTransactionRequest
|
||||
4, // 2: kaspawalletd.GetReceiveAddress:input_type -> GetReceiveAddressRequest
|
||||
8, // 3: kaspawalletd.Shutdown:input_type -> ShutdownRequest
|
||||
6, // 4: kaspawalletd.Broadcast:input_type -> BroadcastRequest
|
||||
1, // 5: kaspawalletd.GetBalance:output_type -> GetBalanceResponse
|
||||
3, // 6: kaspawalletd.CreateUnsignedTransaction:output_type -> CreateUnsignedTransactionResponse
|
||||
5, // 7: kaspawalletd.GetReceiveAddress:output_type -> GetReceiveAddressResponse
|
||||
9, // 8: kaspawalletd.Shutdown:output_type -> ShutdownResponse
|
||||
7, // 9: kaspawalletd.Broadcast:output_type -> BroadcastResponse
|
||||
5, // [5:10] is the sub-list for method output_type
|
||||
0, // [0:5] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
0, // 0: kaspawalletd.GetBalance:input_type -> GetBalanceRequest
|
||||
2, // 1: kaspawalletd.CreateUnsignedTransaction:input_type -> CreateUnsignedTransactionRequest
|
||||
4, // 2: kaspawalletd.ShowAddresses:input_type -> ShowAddressesRequest
|
||||
6, // 3: kaspawalletd.NewAddress:input_type -> NewAddressRequest
|
||||
10, // 4: kaspawalletd.Shutdown:input_type -> ShutdownRequest
|
||||
8, // 5: kaspawalletd.Broadcast:input_type -> BroadcastRequest
|
||||
1, // 6: kaspawalletd.GetBalance:output_type -> GetBalanceResponse
|
||||
3, // 7: kaspawalletd.CreateUnsignedTransaction:output_type -> CreateUnsignedTransactionResponse
|
||||
5, // 8: kaspawalletd.ShowAddresses:output_type -> ShowAddressesResponse
|
||||
7, // 9: kaspawalletd.NewAddress:output_type -> NewAddressResponse
|
||||
11, // 10: kaspawalletd.Shutdown:output_type -> ShutdownResponse
|
||||
9, // 11: kaspawalletd.Broadcast:output_type -> BroadcastResponse
|
||||
6, // [6:12] is the sub-list for method output_type
|
||||
0, // [0:6] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_kaspawalletd_proto_init() }
|
||||
@@ -637,7 +727,7 @@ func file_kaspawalletd_proto_init() {
|
||||
}
|
||||
}
|
||||
file_kaspawalletd_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GetReceiveAddressRequest); i {
|
||||
switch v := v.(*ShowAddressesRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@@ -649,7 +739,7 @@ func file_kaspawalletd_proto_init() {
|
||||
}
|
||||
}
|
||||
file_kaspawalletd_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GetReceiveAddressResponse); i {
|
||||
switch v := v.(*ShowAddressesResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@@ -661,7 +751,7 @@ func file_kaspawalletd_proto_init() {
|
||||
}
|
||||
}
|
||||
file_kaspawalletd_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*BroadcastRequest); i {
|
||||
switch v := v.(*NewAddressRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@@ -673,7 +763,7 @@ func file_kaspawalletd_proto_init() {
|
||||
}
|
||||
}
|
||||
file_kaspawalletd_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*BroadcastResponse); i {
|
||||
switch v := v.(*NewAddressResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@@ -685,7 +775,7 @@ func file_kaspawalletd_proto_init() {
|
||||
}
|
||||
}
|
||||
file_kaspawalletd_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ShutdownRequest); i {
|
||||
switch v := v.(*BroadcastRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@@ -697,6 +787,30 @@ func file_kaspawalletd_proto_init() {
|
||||
}
|
||||
}
|
||||
file_kaspawalletd_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*BroadcastResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_kaspawalletd_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ShutdownRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_kaspawalletd_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ShutdownResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -715,7 +829,7 @@ func file_kaspawalletd_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_kaspawalletd_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 10,
|
||||
NumMessages: 12,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
||||
@@ -5,7 +5,8 @@ option go_package = "github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb";
|
||||
service kaspawalletd {
|
||||
rpc GetBalance (GetBalanceRequest) returns (GetBalanceResponse) {}
|
||||
rpc CreateUnsignedTransaction (CreateUnsignedTransactionRequest) returns (CreateUnsignedTransactionResponse) {}
|
||||
rpc GetReceiveAddress (GetReceiveAddressRequest) returns (GetReceiveAddressResponse) {}
|
||||
rpc ShowAddresses (ShowAddressesRequest) returns (ShowAddressesResponse) {}
|
||||
rpc NewAddress (NewAddressRequest) returns (NewAddressResponse) {}
|
||||
rpc Shutdown (ShutdownRequest) returns (ShutdownResponse) {}
|
||||
rpc Broadcast (BroadcastRequest) returns (BroadcastResponse) {}
|
||||
}
|
||||
@@ -27,10 +28,17 @@ message CreateUnsignedTransactionResponse {
|
||||
bytes unsignedTransaction = 1;
|
||||
}
|
||||
|
||||
message GetReceiveAddressRequest {
|
||||
message ShowAddressesRequest {
|
||||
}
|
||||
|
||||
message GetReceiveAddressResponse {
|
||||
message ShowAddressesResponse {
|
||||
repeated string address = 1;
|
||||
}
|
||||
|
||||
message NewAddressRequest {
|
||||
}
|
||||
|
||||
message NewAddressResponse {
|
||||
string address = 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ import (
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion6
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// KaspawalletdClient is the client API for Kaspawalletd service.
|
||||
//
|
||||
@@ -19,7 +20,8 @@ const _ = grpc.SupportPackageIsVersion6
|
||||
type KaspawalletdClient interface {
|
||||
GetBalance(ctx context.Context, in *GetBalanceRequest, opts ...grpc.CallOption) (*GetBalanceResponse, error)
|
||||
CreateUnsignedTransaction(ctx context.Context, in *CreateUnsignedTransactionRequest, opts ...grpc.CallOption) (*CreateUnsignedTransactionResponse, error)
|
||||
GetReceiveAddress(ctx context.Context, in *GetReceiveAddressRequest, opts ...grpc.CallOption) (*GetReceiveAddressResponse, error)
|
||||
ShowAddresses(ctx context.Context, in *ShowAddressesRequest, opts ...grpc.CallOption) (*ShowAddressesResponse, error)
|
||||
NewAddress(ctx context.Context, in *NewAddressRequest, opts ...grpc.CallOption) (*NewAddressResponse, error)
|
||||
Shutdown(ctx context.Context, in *ShutdownRequest, opts ...grpc.CallOption) (*ShutdownResponse, error)
|
||||
Broadcast(ctx context.Context, in *BroadcastRequest, opts ...grpc.CallOption) (*BroadcastResponse, error)
|
||||
}
|
||||
@@ -50,9 +52,18 @@ func (c *kaspawalletdClient) CreateUnsignedTransaction(ctx context.Context, in *
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *kaspawalletdClient) GetReceiveAddress(ctx context.Context, in *GetReceiveAddressRequest, opts ...grpc.CallOption) (*GetReceiveAddressResponse, error) {
|
||||
out := new(GetReceiveAddressResponse)
|
||||
err := c.cc.Invoke(ctx, "/kaspawalletd/GetReceiveAddress", in, out, opts...)
|
||||
func (c *kaspawalletdClient) ShowAddresses(ctx context.Context, in *ShowAddressesRequest, opts ...grpc.CallOption) (*ShowAddressesResponse, error) {
|
||||
out := new(ShowAddressesResponse)
|
||||
err := c.cc.Invoke(ctx, "/kaspawalletd/ShowAddresses", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *kaspawalletdClient) NewAddress(ctx context.Context, in *NewAddressRequest, opts ...grpc.CallOption) (*NewAddressResponse, error) {
|
||||
out := new(NewAddressResponse)
|
||||
err := c.cc.Invoke(ctx, "/kaspawalletd/NewAddress", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -83,7 +94,8 @@ func (c *kaspawalletdClient) Broadcast(ctx context.Context, in *BroadcastRequest
|
||||
type KaspawalletdServer interface {
|
||||
GetBalance(context.Context, *GetBalanceRequest) (*GetBalanceResponse, error)
|
||||
CreateUnsignedTransaction(context.Context, *CreateUnsignedTransactionRequest) (*CreateUnsignedTransactionResponse, error)
|
||||
GetReceiveAddress(context.Context, *GetReceiveAddressRequest) (*GetReceiveAddressResponse, error)
|
||||
ShowAddresses(context.Context, *ShowAddressesRequest) (*ShowAddressesResponse, error)
|
||||
NewAddress(context.Context, *NewAddressRequest) (*NewAddressResponse, error)
|
||||
Shutdown(context.Context, *ShutdownRequest) (*ShutdownResponse, error)
|
||||
Broadcast(context.Context, *BroadcastRequest) (*BroadcastResponse, error)
|
||||
mustEmbedUnimplementedKaspawalletdServer()
|
||||
@@ -93,25 +105,35 @@ type KaspawalletdServer interface {
|
||||
type UnimplementedKaspawalletdServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedKaspawalletdServer) GetBalance(context.Context, *GetBalanceRequest) (*GetBalanceResponse, error) {
|
||||
func (UnimplementedKaspawalletdServer) GetBalance(context.Context, *GetBalanceRequest) (*GetBalanceResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetBalance not implemented")
|
||||
}
|
||||
func (*UnimplementedKaspawalletdServer) CreateUnsignedTransaction(context.Context, *CreateUnsignedTransactionRequest) (*CreateUnsignedTransactionResponse, error) {
|
||||
func (UnimplementedKaspawalletdServer) CreateUnsignedTransaction(context.Context, *CreateUnsignedTransactionRequest) (*CreateUnsignedTransactionResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CreateUnsignedTransaction not implemented")
|
||||
}
|
||||
func (*UnimplementedKaspawalletdServer) GetReceiveAddress(context.Context, *GetReceiveAddressRequest) (*GetReceiveAddressResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetReceiveAddress not implemented")
|
||||
func (UnimplementedKaspawalletdServer) ShowAddresses(context.Context, *ShowAddressesRequest) (*ShowAddressesResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ShowAddresses not implemented")
|
||||
}
|
||||
func (*UnimplementedKaspawalletdServer) Shutdown(context.Context, *ShutdownRequest) (*ShutdownResponse, error) {
|
||||
func (UnimplementedKaspawalletdServer) NewAddress(context.Context, *NewAddressRequest) (*NewAddressResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method NewAddress not implemented")
|
||||
}
|
||||
func (UnimplementedKaspawalletdServer) Shutdown(context.Context, *ShutdownRequest) (*ShutdownResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Shutdown not implemented")
|
||||
}
|
||||
func (*UnimplementedKaspawalletdServer) Broadcast(context.Context, *BroadcastRequest) (*BroadcastResponse, error) {
|
||||
func (UnimplementedKaspawalletdServer) Broadcast(context.Context, *BroadcastRequest) (*BroadcastResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Broadcast not implemented")
|
||||
}
|
||||
func (*UnimplementedKaspawalletdServer) mustEmbedUnimplementedKaspawalletdServer() {}
|
||||
func (UnimplementedKaspawalletdServer) mustEmbedUnimplementedKaspawalletdServer() {}
|
||||
|
||||
func RegisterKaspawalletdServer(s *grpc.Server, srv KaspawalletdServer) {
|
||||
s.RegisterService(&_Kaspawalletd_serviceDesc, srv)
|
||||
// UnsafeKaspawalletdServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to KaspawalletdServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeKaspawalletdServer interface {
|
||||
mustEmbedUnimplementedKaspawalletdServer()
|
||||
}
|
||||
|
||||
func RegisterKaspawalletdServer(s grpc.ServiceRegistrar, srv KaspawalletdServer) {
|
||||
s.RegisterService(&Kaspawalletd_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _Kaspawalletd_GetBalance_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
@@ -150,20 +172,38 @@ func _Kaspawalletd_CreateUnsignedTransaction_Handler(srv interface{}, ctx contex
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Kaspawalletd_GetReceiveAddress_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetReceiveAddressRequest)
|
||||
func _Kaspawalletd_ShowAddresses_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ShowAddressesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(KaspawalletdServer).GetReceiveAddress(ctx, in)
|
||||
return srv.(KaspawalletdServer).ShowAddresses(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/kaspawalletd/GetReceiveAddress",
|
||||
FullMethod: "/kaspawalletd/ShowAddresses",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(KaspawalletdServer).GetReceiveAddress(ctx, req.(*GetReceiveAddressRequest))
|
||||
return srv.(KaspawalletdServer).ShowAddresses(ctx, req.(*ShowAddressesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Kaspawalletd_NewAddress_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(NewAddressRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(KaspawalletdServer).NewAddress(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/kaspawalletd/NewAddress",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(KaspawalletdServer).NewAddress(ctx, req.(*NewAddressRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
@@ -204,7 +244,10 @@ func _Kaspawalletd_Broadcast_Handler(srv interface{}, ctx context.Context, dec f
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Kaspawalletd_serviceDesc = grpc.ServiceDesc{
|
||||
// Kaspawalletd_ServiceDesc is the grpc.ServiceDesc for Kaspawalletd service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var Kaspawalletd_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "kaspawalletd",
|
||||
HandlerType: (*KaspawalletdServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
@@ -217,8 +260,12 @@ var _Kaspawalletd_serviceDesc = grpc.ServiceDesc{
|
||||
Handler: _Kaspawalletd_CreateUnsignedTransaction_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetReceiveAddress",
|
||||
Handler: _Kaspawalletd_GetReceiveAddress_Handler,
|
||||
MethodName: "ShowAddresses",
|
||||
Handler: _Kaspawalletd_ShowAddresses_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "NewAddress",
|
||||
Handler: _Kaspawalletd_NewAddress_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Shutdown",
|
||||
|
||||
@@ -3,6 +3,7 @@ package server
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
@@ -23,13 +24,39 @@ func (s *server) changeAddress() (util.Address, error) {
|
||||
walletAddr := &walletAddress{
|
||||
index: s.keysFile.LastUsedInternalIndex(),
|
||||
cosignerIndex: s.keysFile.CosignerIndex,
|
||||
keyChain: internalKeychain,
|
||||
keyChain: libkaspawallet.InternalKeychain,
|
||||
}
|
||||
path := s.walletAddressPath(walletAddr)
|
||||
return libkaspawallet.Address(s.params, s.keysFile.ExtendedPublicKeys, s.keysFile.MinimumSignatures, path, s.keysFile.ECDSA)
|
||||
}
|
||||
|
||||
func (s *server) GetReceiveAddress(_ context.Context, request *pb.GetReceiveAddressRequest) (*pb.GetReceiveAddressResponse, error) {
|
||||
func (s *server) ShowAddresses(_ context.Context, request *pb.ShowAddressesRequest) (*pb.ShowAddressesResponse, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
if !s.isSynced() {
|
||||
return nil, errors.New("server is not synced")
|
||||
}
|
||||
|
||||
addresses := make([]string, 0)
|
||||
for i := uint32(1); i <= s.keysFile.LastUsedExternalIndex(); i++ {
|
||||
walletAddr := &walletAddress{
|
||||
index: i,
|
||||
cosignerIndex: s.keysFile.CosignerIndex,
|
||||
keyChain: libkaspawallet.ExternalKeychain,
|
||||
}
|
||||
path := s.walletAddressPath(walletAddr)
|
||||
address, err := libkaspawallet.Address(s.params, s.keysFile.ExtendedPublicKeys, s.keysFile.MinimumSignatures, path, s.keysFile.ECDSA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addresses = append(addresses, address.String())
|
||||
}
|
||||
|
||||
return &pb.ShowAddressesResponse{Address: addresses}, nil
|
||||
}
|
||||
|
||||
func (s *server) NewAddress(_ context.Context, request *pb.NewAddressRequest) (*pb.NewAddressResponse, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
@@ -50,7 +77,7 @@ func (s *server) GetReceiveAddress(_ context.Context, request *pb.GetReceiveAddr
|
||||
walletAddr := &walletAddress{
|
||||
index: s.keysFile.LastUsedExternalIndex(),
|
||||
cosignerIndex: s.keysFile.CosignerIndex,
|
||||
keyChain: externalKeychain,
|
||||
keyChain: libkaspawallet.ExternalKeychain,
|
||||
}
|
||||
path := s.walletAddressPath(walletAddr)
|
||||
address, err := libkaspawallet.Address(s.params, s.keysFile.ExtendedPublicKeys, s.keysFile.MinimumSignatures, path, s.keysFile.ECDSA)
|
||||
@@ -58,7 +85,7 @@ func (s *server) GetReceiveAddress(_ context.Context, request *pb.GetReceiveAddr
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pb.GetReceiveAddressResponse{Address: address.String()}, nil
|
||||
return &pb.NewAddressResponse{Address: address.String()}, nil
|
||||
}
|
||||
|
||||
func (s *server) walletAddressString(wAddr *walletAddress) (string, error) {
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/pkg/errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// externalKeychain is the key chain that is used to create receive addresses
|
||||
externalKeychain = 0
|
||||
// internalKeychain is used to create change addresses
|
||||
internalKeychain = 1
|
||||
)
|
||||
|
||||
var keyChains = []uint8{externalKeychain, internalKeychain}
|
||||
var keyChains = []uint8{libkaspawallet.ExternalKeychain, libkaspawallet.InternalKeychain}
|
||||
|
||||
type walletAddressSet map[string]*walletAddress
|
||||
|
||||
@@ -180,7 +176,7 @@ func (s *server) updateLastUsedIndexes(addressSet walletAddressSet,
|
||||
continue
|
||||
}
|
||||
|
||||
if walletAddress.keyChain == externalKeychain {
|
||||
if walletAddress.keyChain == libkaspawallet.ExternalKeychain {
|
||||
if walletAddress.index > lastUsedExternalIndex {
|
||||
lastUsedExternalIndex = walletAddress.index
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ func encryptMnemonic(mnemonic string, password []byte) (*EncryptedMnemonic, erro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
aead, err := getAEAD(password, salt)
|
||||
aead, err := getAEAD(defaultNumThreads, password, salt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -6,10 +6,12 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/utils"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/utils"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
@@ -22,6 +24,9 @@ var (
|
||||
defaultAppDir = util.AppDir("kaspawallet", false)
|
||||
)
|
||||
|
||||
// LastVersion is the most up to date file format version
|
||||
const LastVersion = 1
|
||||
|
||||
func defaultKeysFile(netParams *dagconfig.Params) string {
|
||||
return filepath.Join(defaultAppDir, netParams.Name, "keys.json")
|
||||
}
|
||||
@@ -32,6 +37,8 @@ type encryptedPrivateKeyJSON struct {
|
||||
}
|
||||
|
||||
type keysFileJSON struct {
|
||||
Version uint32 `json:"version"`
|
||||
NumThreads uint8 `json:"numThreads,omitempty"` // This field is ignored for versions different from 0. See more details at the function `numThreads`.
|
||||
EncryptedPrivateKeys []*encryptedPrivateKeyJSON `json:"encryptedMnemonics"`
|
||||
ExtendedPublicKeys []string `json:"publicKeys"`
|
||||
MinimumSignatures uint32 `json:"minimumSignatures"`
|
||||
@@ -49,6 +56,8 @@ type EncryptedMnemonic struct {
|
||||
|
||||
// File holds all the data related to the wallet keys
|
||||
type File struct {
|
||||
Version uint32
|
||||
NumThreads uint8 // This field is ignored for versions different than 0
|
||||
EncryptedMnemonics []*EncryptedMnemonic
|
||||
ExtendedPublicKeys []string
|
||||
MinimumSignatures uint32
|
||||
@@ -69,6 +78,8 @@ func (d *File) toJSON() *keysFileJSON {
|
||||
}
|
||||
|
||||
return &keysFileJSON{
|
||||
Version: d.Version,
|
||||
NumThreads: d.NumThreads,
|
||||
EncryptedPrivateKeys: encryptedPrivateKeysJSON,
|
||||
ExtendedPublicKeys: d.ExtendedPublicKeys,
|
||||
MinimumSignatures: d.MinimumSignatures,
|
||||
@@ -79,7 +90,26 @@ func (d *File) toJSON() *keysFileJSON {
|
||||
}
|
||||
}
|
||||
|
||||
// NewFileFromMnemonic generates a new File from the given mnemonic string
|
||||
func NewFileFromMnemonic(params *dagconfig.Params, mnemonic string, password string) (*File, error) {
|
||||
encryptedMnemonics, extendedPublicKeys, err :=
|
||||
encryptedMnemonicExtendedPublicKeyPairs(params, []string{mnemonic}, password, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &File{
|
||||
Version: LastVersion,
|
||||
NumThreads: defaultNumThreads,
|
||||
EncryptedMnemonics: encryptedMnemonics,
|
||||
ExtendedPublicKeys: extendedPublicKeys,
|
||||
MinimumSignatures: 1,
|
||||
ECDSA: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *File) fromJSON(fileJSON *keysFileJSON) error {
|
||||
d.Version = fileJSON.Version
|
||||
d.NumThreads = fileJSON.NumThreads
|
||||
d.MinimumSignatures = fileJSON.MinimumSignatures
|
||||
d.ECDSA = fileJSON.ECDSA
|
||||
d.ExtendedPublicKeys = fileJSON.ExtendedPublicKeys
|
||||
@@ -181,10 +211,20 @@ func (d *File) DecryptMnemonics(cmdLinePassword string) ([]string, error) {
|
||||
if len(password) == 0 {
|
||||
password = getPassword("Password:")
|
||||
}
|
||||
|
||||
var numThreads uint8
|
||||
if len(d.EncryptedMnemonics) > 0 {
|
||||
var err error
|
||||
numThreads, err = d.numThreads(password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
privateKeys := make([]string, len(d.EncryptedMnemonics))
|
||||
for i, encryptedPrivateKey := range d.EncryptedMnemonics {
|
||||
var err error
|
||||
privateKeys[i], err = decryptMnemonic(encryptedPrivateKey, password)
|
||||
privateKeys[i], err = decryptMnemonic(numThreads, encryptedPrivateKey, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -278,13 +318,72 @@ func (d *File) Save() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAEAD(password, salt []byte) (cipher.AEAD, error) {
|
||||
key := argon2.IDKey(password, salt, 1, 64*1024, uint8(runtime.NumCPU()), 32)
|
||||
const defaultNumThreads = 8
|
||||
|
||||
func (d *File) numThreads(password []byte) (uint8, error) {
|
||||
// There's a bug in v0 wallets where the number of threads
|
||||
// was determined by the number of logical CPUs at the machine,
|
||||
// which made the authentication non-deterministic across platforms.
|
||||
// In order to solve it we introduce v1 where the number of threads
|
||||
// is constant, and brute force the number of threads in v0. After we
|
||||
// find the right amount via brute force we save the result to the file.
|
||||
|
||||
if d.Version != 0 {
|
||||
return defaultNumThreads, nil
|
||||
}
|
||||
|
||||
numThreads, err := d.detectNumThreads(password, d.EncryptedMnemonics[0])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
d.NumThreads = numThreads
|
||||
err = d.Save()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return numThreads, nil
|
||||
}
|
||||
|
||||
func (d *File) detectNumThreads(password []byte, encryptedMnemonic *EncryptedMnemonic) (uint8, error) {
|
||||
firstGuessNumThreads := d.NumThreads
|
||||
if d.NumThreads == 0 {
|
||||
firstGuessNumThreads = uint8(runtime.NumCPU())
|
||||
}
|
||||
_, err := decryptMnemonic(firstGuessNumThreads, encryptedMnemonic, password)
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), "message authentication failed") {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
return firstGuessNumThreads, nil
|
||||
}
|
||||
|
||||
for numThreadsGuess := uint8(1); ; numThreadsGuess++ {
|
||||
if numThreadsGuess == firstGuessNumThreads {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err := decryptMnemonic(numThreadsGuess, encryptedMnemonic, password)
|
||||
if err != nil {
|
||||
const maxTries = 32
|
||||
if numThreadsGuess == maxTries || !strings.Contains(err.Error(), "message authentication failed") {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
return numThreadsGuess, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getAEAD(threads uint8, password, salt []byte) (cipher.AEAD, error) {
|
||||
key := argon2.IDKey(password, salt, 1, 64*1024, threads, 32)
|
||||
return chacha20poly1305.NewX(key)
|
||||
}
|
||||
|
||||
func decryptMnemonic(encryptedPrivateKey *EncryptedMnemonic, password []byte) (string, error) {
|
||||
aead, err := getAEAD(password, encryptedPrivateKey.salt)
|
||||
func decryptMnemonic(numThreads uint8, encryptedPrivateKey *EncryptedMnemonic, password []byte) (string, error) {
|
||||
aead, err := getAEAD(numThreads, password, encryptedPrivateKey.salt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
8
cmd/kaspawallet/libkaspawallet/keychains.go
Normal file
8
cmd/kaspawallet/libkaspawallet/keychains.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package libkaspawallet
|
||||
|
||||
const (
|
||||
// ExternalKeychain is the key chain that is used to create receive addresses
|
||||
ExternalKeychain = 0
|
||||
// InternalKeychain is used to create change addresses
|
||||
InternalKeychain = 1
|
||||
)
|
||||
@@ -163,7 +163,7 @@ func TestMultisig(t *testing.T) {
|
||||
t.Fatalf("Expected extractedSignedTxOneStep and extractedSignedTxStep2 IDs to be equal")
|
||||
}
|
||||
|
||||
_, insertionResult, err := tc.AddBlock([]*externalapi.DomainHash{block1Hash}, nil, []*externalapi.DomainTransaction{extractedSignedTxStep2})
|
||||
_, virtualChangeSet, err := tc.AddBlock([]*externalapi.DomainHash{block1Hash}, nil, []*externalapi.DomainTransaction{extractedSignedTxStep2})
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
@@ -172,7 +172,7 @@ func TestMultisig(t *testing.T) {
|
||||
TransactionID: *consensushashing.TransactionID(extractedSignedTxStep2),
|
||||
Index: 0,
|
||||
}
|
||||
if !insertionResult.VirtualUTXODiff.ToAdd().Contains(addedUTXO) {
|
||||
if !virtualChangeSet.VirtualUTXODiff.ToAdd().Contains(addedUTXO) {
|
||||
t.Fatalf("Transaction wasn't accepted in the DAG")
|
||||
}
|
||||
})
|
||||
@@ -294,7 +294,7 @@ func TestP2PK(t *testing.T) {
|
||||
t.Fatalf("ExtractTransaction: %+v", err)
|
||||
}
|
||||
|
||||
_, insertionResult, err := tc.AddBlock([]*externalapi.DomainHash{block1Hash}, nil, []*externalapi.DomainTransaction{tx})
|
||||
_, virtualChangeSet, err := tc.AddBlock([]*externalapi.DomainHash{block1Hash}, nil, []*externalapi.DomainTransaction{tx})
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
@@ -303,7 +303,7 @@ func TestP2PK(t *testing.T) {
|
||||
TransactionID: *consensushashing.TransactionID(tx),
|
||||
Index: 0,
|
||||
}
|
||||
if !insertionResult.VirtualUTXODiff.ToAdd().Contains(addedUTXO) {
|
||||
if !virtualChangeSet.VirtualUTXODiff.ToAdd().Contains(addedUTXO) {
|
||||
t.Fatalf("Transaction wasn't accepted in the DAG")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -19,8 +19,10 @@ func main() {
|
||||
err = sign(config.(*signConfig))
|
||||
case broadcastSubCmd:
|
||||
err = broadcast(config.(*broadcastConfig))
|
||||
case showAddressSubCmd:
|
||||
err = showAddress(config.(*showAddressConfig))
|
||||
case showAddressesSubCmd:
|
||||
err = showAddresses(config.(*showAddressesConfig))
|
||||
case newAddressSubCmd:
|
||||
err = newAddress(config.(*newAddressConfig))
|
||||
case dumpUnencryptedDataSubCmd:
|
||||
err = dumpUnencryptedData(config.(*dumpUnencryptedDataConfig))
|
||||
case startDaemonSubCmd:
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
|
||||
)
|
||||
|
||||
func showAddress(conf *showAddressConfig) error {
|
||||
func newAddress(conf *newAddressConfig) error {
|
||||
daemonClient, tearDown, err := client.Connect(conf.DaemonAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -17,11 +17,11 @@ func showAddress(conf *showAddressConfig) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), daemonTimeout)
|
||||
defer cancel()
|
||||
|
||||
response, err := daemonClient.GetReceiveAddress(ctx, &pb.GetReceiveAddressRequest{})
|
||||
response, err := daemonClient.NewAddress(ctx, &pb.NewAddressRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Address:\n%s\n", response.Address)
|
||||
fmt.Printf("New address:\n%s\n", response.Address)
|
||||
return nil
|
||||
}
|
||||
30
cmd/kaspawallet/show_addresses.go
Normal file
30
cmd/kaspawallet/show_addresses.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/client"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
|
||||
)
|
||||
|
||||
func showAddresses(conf *showAddressesConfig) error {
|
||||
daemonClient, tearDown, err := client.Connect(conf.DaemonAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), daemonTimeout)
|
||||
defer cancel()
|
||||
|
||||
response, err := daemonClient.ShowAddresses(ctx, &pb.ShowAddressesRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Addresses (%d):\n", len(response.Address))
|
||||
for _, address := range response.Address {
|
||||
fmt.Println(address)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -56,7 +56,7 @@ type consensus struct {
|
||||
daaBlocksStore model.DAABlocksStore
|
||||
}
|
||||
|
||||
func (s *consensus) ValidateAndInsertBlockWithTrustedData(block *externalapi.BlockWithTrustedData, validateUTXO bool) (*externalapi.BlockInsertionResult, error) {
|
||||
func (s *consensus) ValidateAndInsertBlockWithTrustedData(block *externalapi.BlockWithTrustedData, validateUTXO bool) (*externalapi.VirtualChangeSet, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
@@ -137,11 +137,18 @@ func (s *consensus) Init(skipAddingGenesis bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *consensus) PruningPointAndItsAnticoneWithTrustedData() ([]*externalapi.BlockWithTrustedData, error) {
|
||||
func (s *consensus) PruningPointAndItsAnticone() ([]*externalapi.DomainHash, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
return s.pruningManager.PruningPointAndItsAnticoneWithTrustedData()
|
||||
return s.pruningManager.PruningPointAndItsAnticone()
|
||||
}
|
||||
|
||||
func (s *consensus) BlockWithTrustedData(blockHash *externalapi.DomainHash) (*externalapi.BlockWithTrustedData, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
return s.pruningManager.BlockWithTrustedData(model.NewStagingArea(), blockHash)
|
||||
}
|
||||
|
||||
// BuildBlock builds a block over the current state, with the transactions
|
||||
@@ -157,7 +164,7 @@ func (s *consensus) BuildBlock(coinbaseData *externalapi.DomainCoinbaseData,
|
||||
|
||||
// ValidateAndInsertBlock validates the given block and, if valid, applies it
|
||||
// to the current state
|
||||
func (s *consensus) ValidateAndInsertBlock(block *externalapi.DomainBlock, shouldValidateAgainstUTXO bool) (*externalapi.BlockInsertionResult, error) {
|
||||
func (s *consensus) ValidateAndInsertBlock(block *externalapi.DomainBlock, shouldValidateAgainstUTXO bool) (*externalapi.VirtualChangeSet, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
@@ -713,26 +720,13 @@ func (s *consensus) PopulateMass(transaction *externalapi.DomainTransaction) {
|
||||
s.transactionValidator.PopulateMass(transaction)
|
||||
}
|
||||
|
||||
func (s *consensus) ResolveVirtual() error {
|
||||
func (s *consensus) ResolveVirtual() (*externalapi.VirtualChangeSet, bool, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
// In order to prevent a situation that the consensus lock is held for too much time, we
|
||||
// release the lock each time resolve 100 blocks.
|
||||
for {
|
||||
var isCompletelyResolved bool
|
||||
var err error
|
||||
func() {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
isCompletelyResolved, err = s.consensusStateManager.ResolveVirtual(100)
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isCompletelyResolved {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return s.consensusStateManager.ResolveVirtual(100)
|
||||
}
|
||||
|
||||
func (s *consensus) BuildPruningPointProof() (*externalapi.PruningPointProof, error) {
|
||||
|
||||
@@ -6,7 +6,9 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
|
||||
"github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||
"github.com/kaspanet/kaspad/util/staging"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var reachabilityDataBucketName = []byte("reachability-data")
|
||||
@@ -50,6 +52,8 @@ func (rds *reachabilityDataStore) IsStaged(stagingArea *model.StagingArea) bool
|
||||
return rds.stagingShard(stagingArea).isStaged()
|
||||
}
|
||||
|
||||
var errNotFound = errors.Wrap(database.ErrNotFound, "reachability data not found")
|
||||
|
||||
// ReachabilityData returns the reachabilityData associated with the given blockHash
|
||||
func (rds *reachabilityDataStore) ReachabilityData(dbContext model.DBReader, stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (model.ReachabilityData, error) {
|
||||
stagingShard := rds.stagingShard(stagingArea)
|
||||
@@ -59,10 +63,16 @@ func (rds *reachabilityDataStore) ReachabilityData(dbContext model.DBReader, sta
|
||||
}
|
||||
|
||||
if reachabilityData, ok := rds.reachabilityDataCache.Get(blockHash); ok {
|
||||
if reachabilityData == nil {
|
||||
return nil, errNotFound
|
||||
}
|
||||
return reachabilityData.(model.ReachabilityData), nil
|
||||
}
|
||||
|
||||
reachabilityDataBytes, err := dbContext.Get(rds.reachabilityDataBlockHashAsKey(blockHash))
|
||||
if database.IsNotFoundError(err) {
|
||||
rds.reachabilityDataCache.Add(blockHash, nil)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -76,17 +86,15 @@ func (rds *reachabilityDataStore) ReachabilityData(dbContext model.DBReader, sta
|
||||
}
|
||||
|
||||
func (rds *reachabilityDataStore) HasReachabilityData(dbContext model.DBReader, stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (bool, error) {
|
||||
stagingShard := rds.stagingShard(stagingArea)
|
||||
|
||||
if _, ok := stagingShard.reachabilityData[*blockHash]; ok {
|
||||
return true, nil
|
||||
_, err := rds.ReachabilityData(dbContext, stagingArea, blockHash)
|
||||
if database.IsNotFoundError(err) {
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if rds.reachabilityDataCache.Has(blockHash) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return dbContext.Has(rds.reachabilityDataBlockHashAsKey(blockHash))
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// ReachabilityReindexRoot returns the current reachability reindex root
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/daawindowstore"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/blockparentbuilder"
|
||||
parentssanager "github.com/kaspanet/kaspad/domain/consensus/processes/parentsmanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/pruningproofmanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"io/ioutil"
|
||||
@@ -158,12 +159,16 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
|
||||
dagTraversalManager := dagTraversalManagers[0]
|
||||
|
||||
// Processes
|
||||
parentsManager := parentssanager.New(config.GenesisHash)
|
||||
blockParentBuilder := blockparentbuilder.New(
|
||||
dbManager,
|
||||
blockHeaderStore,
|
||||
dagTopologyManager,
|
||||
parentsManager,
|
||||
reachabilityDataStore,
|
||||
pruningStore,
|
||||
|
||||
config.GenesisHash,
|
||||
)
|
||||
pastMedianTimeManager := f.pastMedianTimeConsructor(
|
||||
config.TimestampDeviationTolerance,
|
||||
@@ -302,6 +307,7 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
|
||||
config.MaxBlockParents,
|
||||
config.TimestampDeviationTolerance,
|
||||
config.TargetTimePerBlock,
|
||||
config.IgnoreHeaderMass,
|
||||
|
||||
dbManager,
|
||||
difficultyManager,
|
||||
@@ -316,6 +322,7 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
|
||||
finalityManager,
|
||||
blockParentBuilder,
|
||||
pruningManager,
|
||||
parentsManager,
|
||||
|
||||
pruningStore,
|
||||
blockStore,
|
||||
@@ -403,6 +410,7 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
|
||||
ghostdagManagers,
|
||||
reachabilityManagers,
|
||||
dagTraversalManagers,
|
||||
parentsManager,
|
||||
|
||||
ghostdagDataStores,
|
||||
pruningStore,
|
||||
@@ -581,7 +589,7 @@ func dagStores(config *Config,
|
||||
ghostdagDataStores[i] = ghostdagdatastore.New(prefixBucket, ghostdagDataCacheSize, preallocateCaches)
|
||||
} else {
|
||||
blockRelationStores[i] = blockrelationstore.New(prefixBucket, 200, false)
|
||||
reachabilityDataStores[i] = reachabilitydatastore.New(prefixBucket, 200, false)
|
||||
reachabilityDataStores[i] = reachabilitydatastore.New(prefixBucket, pruningWindowSizePlusFinalityDepthForCache, false)
|
||||
ghostdagDataStores[i] = ghostdagdatastore.New(prefixBucket, 200, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ import (
|
||||
|
||||
func TestFinality(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
// Set finalityInterval to 50 blocks, so that test runs quickly
|
||||
consensusConfig.FinalityDuration = 50 * consensusConfig.TargetTimePerBlock
|
||||
// Set finalityInterval to 20 blocks, so that test runs quickly
|
||||
consensusConfig.FinalityDuration = 20 * consensusConfig.TargetTimePerBlock
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
consensus, teardown, err := factory.NewTestConsensus(consensusConfig, "TestFinality")
|
||||
@@ -180,7 +180,8 @@ func TestFinality(t *testing.T) {
|
||||
func TestBoundedMergeDepth(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
// Set finalityInterval to 50 blocks, so that test runs quickly
|
||||
consensusConfig.FinalityDuration = 50 * consensusConfig.TargetTimePerBlock
|
||||
consensusConfig.K = 5
|
||||
consensusConfig.FinalityDuration = 7 * consensusConfig.TargetTimePerBlock
|
||||
finalityInterval := int(consensusConfig.FinalityDepth())
|
||||
|
||||
if int(consensusConfig.K) >= finalityInterval {
|
||||
|
||||
@@ -58,7 +58,6 @@ type BlockHeader interface {
|
||||
type BaseBlockHeader interface {
|
||||
Version() uint16
|
||||
Parents() []BlockLevelParents
|
||||
ParentsAtLevel(level int) BlockLevelParents
|
||||
DirectParents() BlockLevelParents
|
||||
HashMerkleRoot() *DomainHash
|
||||
AcceptedIDMerkleRoot() *DomainHash
|
||||
@@ -70,6 +69,7 @@ type BaseBlockHeader interface {
|
||||
BlueScore() uint64
|
||||
BlueWork() *big.Int
|
||||
PruningPoint() *DomainHash
|
||||
BlockLevel() int
|
||||
Equal(other BaseBlockHeader) bool
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ package externalapi
|
||||
type Consensus interface {
|
||||
Init(skipAddingGenesis bool) error
|
||||
BuildBlock(coinbaseData *DomainCoinbaseData, transactions []*DomainTransaction) (*DomainBlock, error)
|
||||
ValidateAndInsertBlock(block *DomainBlock, shouldValidateAgainstUTXO bool) (*BlockInsertionResult, error)
|
||||
ValidateAndInsertBlockWithTrustedData(block *BlockWithTrustedData, validateUTXO bool) (*BlockInsertionResult, error)
|
||||
ValidateAndInsertBlock(block *DomainBlock, shouldValidateAgainstUTXO bool) (*VirtualChangeSet, error)
|
||||
ValidateAndInsertBlockWithTrustedData(block *BlockWithTrustedData, validateUTXO bool) (*VirtualChangeSet, error)
|
||||
ValidateTransactionAndPopulateWithConsensusData(transaction *DomainTransaction) error
|
||||
ImportPruningPoints(pruningPoints []BlockHeader) error
|
||||
BuildPruningPointProof() (*PruningPointProof, error)
|
||||
@@ -25,7 +25,8 @@ type Consensus interface {
|
||||
GetVirtualUTXOs(expectedVirtualParents []*DomainHash, fromOutpoint *DomainOutpoint, limit int) ([]*OutpointAndUTXOEntryPair, error)
|
||||
PruningPoint() (*DomainHash, error)
|
||||
PruningPointHeaders() ([]BlockHeader, error)
|
||||
PruningPointAndItsAnticoneWithTrustedData() ([]*BlockWithTrustedData, error)
|
||||
PruningPointAndItsAnticone() ([]*DomainHash, error)
|
||||
BlockWithTrustedData(blockHash *DomainHash) (*BlockWithTrustedData, error)
|
||||
ClearImportedPruningPointData() error
|
||||
AppendImportedPruningPointUTXOs(outpointAndUTXOEntryPairs []*OutpointAndUTXOEntryPair) error
|
||||
ValidateAndInsertImportedPruningPoint(newPruningPoint *DomainHash) error
|
||||
@@ -45,5 +46,5 @@ type Consensus interface {
|
||||
Anticone(blockHash *DomainHash) ([]*DomainHash, error)
|
||||
EstimateNetworkHashesPerSecond(startHash *DomainHash, windowSize int) (uint64, error)
|
||||
PopulateMass(transaction *DomainTransaction)
|
||||
ResolveVirtual() error
|
||||
ResolveVirtual() (*VirtualChangeSet, bool, error)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package externalapi
|
||||
|
||||
// BlockInsertionResult is auxiliary data returned from ValidateAndInsertBlock
|
||||
type BlockInsertionResult struct {
|
||||
// VirtualChangeSet is auxiliary data returned from ValidateAndInsertBlock and ResolveVirtual
|
||||
type VirtualChangeSet struct {
|
||||
VirtualSelectedParentChainChanges *SelectedChainPath
|
||||
VirtualUTXODiff UTXODiff
|
||||
VirtualParents []*DomainHash
|
||||
@@ -5,5 +5,7 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
// BlockParentBuilder exposes a method to build super-block parents for
|
||||
// a given set of direct parents
|
||||
type BlockParentBuilder interface {
|
||||
BuildParents(stagingArea *StagingArea, directParentHashes []*externalapi.DomainHash) ([]externalapi.BlockLevelParents, error)
|
||||
BuildParents(stagingArea *StagingArea,
|
||||
daaScore uint64,
|
||||
directParentHashes []*externalapi.DomainHash) ([]externalapi.BlockLevelParents, error)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// BlockProcessor is responsible for processing incoming blocks
|
||||
type BlockProcessor interface {
|
||||
ValidateAndInsertBlock(block *externalapi.DomainBlock, shouldValidateAgainstUTXO bool) (*externalapi.BlockInsertionResult, error)
|
||||
ValidateAndInsertBlock(block *externalapi.DomainBlock, shouldValidateAgainstUTXO bool) (*externalapi.VirtualChangeSet, error)
|
||||
ValidateAndInsertImportedPruningPoint(newPruningPoint *externalapi.DomainHash) error
|
||||
ValidateAndInsertBlockWithTrustedData(block *externalapi.BlockWithTrustedData, validateUTXO bool) (*externalapi.BlockInsertionResult, error)
|
||||
ValidateAndInsertBlockWithTrustedData(block *externalapi.BlockWithTrustedData, validateUTXO bool) (*externalapi.VirtualChangeSet, error)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
// coinbase transactions
|
||||
type CoinbaseManager interface {
|
||||
ExpectedCoinbaseTransaction(stagingArea *StagingArea, blockHash *externalapi.DomainHash,
|
||||
coinbaseData *externalapi.DomainCoinbaseData, blockPruningPoint *externalapi.DomainHash) (*externalapi.DomainTransaction, error)
|
||||
CalcBlockSubsidy(stagingArea *StagingArea, blockHash *externalapi.DomainHash, blockPruningPoint *externalapi.DomainHash) (uint64, error)
|
||||
coinbaseData *externalapi.DomainCoinbaseData) (*externalapi.DomainTransaction, error)
|
||||
CalcBlockSubsidy(blockHash *externalapi.DomainHash) (uint64, error)
|
||||
ExtractCoinbaseDataBlueScoreAndSubsidy(coinbaseTx *externalapi.DomainTransaction) (blueScore uint64, coinbaseData *externalapi.DomainCoinbaseData, subsidy uint64, err error)
|
||||
}
|
||||
|
||||
@@ -13,5 +13,5 @@ type ConsensusStateManager interface {
|
||||
GetVirtualSelectedParentChainFromBlock(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (*externalapi.SelectedChainPath, error)
|
||||
RecoverUTXOIfRequired() error
|
||||
ReverseUTXODiffs(tipHash *externalapi.DomainHash, reversalData *UTXODiffReversalData) error
|
||||
ResolveVirtual(maxBlocksToResolve uint64) (bool, error)
|
||||
ResolveVirtual(maxBlocksToResolve uint64) (*externalapi.VirtualChangeSet, bool, error)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ type DAGTraversalManager interface {
|
||||
LowestChainBlockAboveOrEqualToBlueScore(stagingArea *StagingArea, highHash *externalapi.DomainHash, blueScore uint64) (*externalapi.DomainHash, error)
|
||||
// SelectedChildIterator should return a BlockIterator that iterates
|
||||
// from lowHash (exclusive) to highHash (inclusive) over highHash's selected parent chain
|
||||
SelectedChildIterator(stagingArea *StagingArea, highHash, lowHash *externalapi.DomainHash) (BlockIterator, error)
|
||||
SelectedChildIterator(stagingArea *StagingArea, highHash, lowHash *externalapi.DomainHash, includeLowHash bool) (BlockIterator, error)
|
||||
SelectedChild(stagingArea *StagingArea, highHash, lowHash *externalapi.DomainHash) (*externalapi.DomainHash, error)
|
||||
AnticoneFromBlocks(stagingArea *StagingArea, tips []*externalapi.DomainHash, blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error)
|
||||
AnticoneFromVirtualPOV(stagingArea *StagingArea, blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package model
|
||||
|
||||
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// ParentsManager lets is a wrapper above header parents that replaces empty parents with genesis when needed.
|
||||
type ParentsManager interface {
|
||||
ParentsAtLevel(blockHeader externalapi.BlockHeader, level int) externalapi.BlockLevelParents
|
||||
Parents(blockHeader externalapi.BlockHeader) []externalapi.BlockLevelParents
|
||||
}
|
||||
@@ -12,6 +12,7 @@ type PruningManager interface {
|
||||
AppendImportedPruningPointUTXOs(outpointAndUTXOEntryPairs []*externalapi.OutpointAndUTXOEntryPair) error
|
||||
UpdatePruningPointIfRequired() error
|
||||
PruneAllBlocksBelow(stagingArea *StagingArea, pruningPointHash *externalapi.DomainHash) error
|
||||
PruningPointAndItsAnticoneWithTrustedData() ([]*externalapi.BlockWithTrustedData, error)
|
||||
PruningPointAndItsAnticone() ([]*externalapi.DomainHash, error)
|
||||
ExpectedHeaderPruningPoint(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (*externalapi.DomainHash, error)
|
||||
BlockWithTrustedData(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (*externalapi.BlockWithTrustedData, error)
|
||||
}
|
||||
|
||||
@@ -41,12 +41,12 @@ type TestConsensus interface {
|
||||
// AddBlock builds a block with given information, solves it, and adds to the DAG.
|
||||
// Returns the hash of the added block
|
||||
AddBlock(parentHashes []*externalapi.DomainHash, coinbaseData *externalapi.DomainCoinbaseData,
|
||||
transactions []*externalapi.DomainTransaction) (*externalapi.DomainHash, *externalapi.BlockInsertionResult, error)
|
||||
transactions []*externalapi.DomainTransaction) (*externalapi.DomainHash, *externalapi.VirtualChangeSet, error)
|
||||
|
||||
AddUTXOInvalidHeader(parentHashes []*externalapi.DomainHash) (*externalapi.DomainHash, *externalapi.BlockInsertionResult, error)
|
||||
AddUTXOInvalidHeader(parentHashes []*externalapi.DomainHash) (*externalapi.DomainHash, *externalapi.VirtualChangeSet, error)
|
||||
|
||||
AddUTXOInvalidBlock(parentHashes []*externalapi.DomainHash) (*externalapi.DomainHash,
|
||||
*externalapi.BlockInsertionResult, error)
|
||||
*externalapi.VirtualChangeSet, error)
|
||||
|
||||
MineJSON(r io.Reader, blockType MineJSONBlockType) (tips []*externalapi.DomainHash, err error)
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ func (bb *blockBuilder) buildBlock(stagingArea *model.StagingArea, coinbaseData
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
coinbase, err := bb.newBlockCoinbaseTransaction(stagingArea, coinbaseData, newBlockPruningPoint)
|
||||
coinbase, err := bb.newBlockCoinbaseTransaction(stagingArea, coinbaseData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -175,18 +175,24 @@ func (bb *blockBuilder) validateTransaction(
|
||||
}
|
||||
|
||||
func (bb *blockBuilder) newBlockCoinbaseTransaction(stagingArea *model.StagingArea,
|
||||
coinbaseData *externalapi.DomainCoinbaseData, blockPruningPoint *externalapi.DomainHash) (*externalapi.DomainTransaction, error) {
|
||||
coinbaseData *externalapi.DomainCoinbaseData) (*externalapi.DomainTransaction, error) {
|
||||
|
||||
return bb.coinbaseManager.ExpectedCoinbaseTransaction(stagingArea, model.VirtualBlockHash, coinbaseData, blockPruningPoint)
|
||||
return bb.coinbaseManager.ExpectedCoinbaseTransaction(stagingArea, model.VirtualBlockHash, coinbaseData)
|
||||
}
|
||||
|
||||
func (bb *blockBuilder) buildHeader(stagingArea *model.StagingArea, transactions []*externalapi.DomainTransaction,
|
||||
newBlockPruningPoint *externalapi.DomainHash) (externalapi.BlockHeader, error) {
|
||||
|
||||
parents, err := bb.newBlockParents(stagingArea)
|
||||
daaScore, err := bb.newBlockDAAScore(stagingArea)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parents, err := bb.newBlockParents(stagingArea, daaScore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
timeInMilliseconds, err := bb.newBlockTime(stagingArea)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -204,10 +210,6 @@ func (bb *blockBuilder) buildHeader(stagingArea *model.StagingArea, transactions
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
daaScore, err := bb.newBlockDAAScore(stagingArea)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blueWork, err := bb.newBlockBlueWork(stagingArea)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -233,12 +235,12 @@ func (bb *blockBuilder) buildHeader(stagingArea *model.StagingArea, transactions
|
||||
), nil
|
||||
}
|
||||
|
||||
func (bb *blockBuilder) newBlockParents(stagingArea *model.StagingArea) ([]externalapi.BlockLevelParents, error) {
|
||||
func (bb *blockBuilder) newBlockParents(stagingArea *model.StagingArea, daaScore uint64) ([]externalapi.BlockLevelParents, error) {
|
||||
virtualBlockRelations, err := bb.blockRelationStore.BlockRelation(bb.databaseContext, stagingArea, model.VirtualBlockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bb.blockParentBuilder.BuildParents(stagingArea, virtualBlockRelations.Parents)
|
||||
return bb.blockParentBuilder.BuildParents(stagingArea, daaScore, virtualBlockRelations.Parents)
|
||||
}
|
||||
|
||||
func (bb *blockBuilder) newBlockTime(stagingArea *model.StagingArea) (int64, error) {
|
||||
|
||||
@@ -83,7 +83,7 @@ func (bb *testBlockBuilder) buildUTXOInvalidHeader(stagingArea *model.StagingAre
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parents, err := bb.blockParentBuilder.BuildParents(stagingArea, parentHashes)
|
||||
parents, err := bb.blockParentBuilder.BuildParents(stagingArea, daaScore, parentHashes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -200,11 +200,7 @@ func (bb *testBlockBuilder) buildBlockWithParents(stagingArea *model.StagingArea
|
||||
|
||||
bb.acceptanceDataStore.Stage(stagingArea, tempBlockHash, acceptanceData)
|
||||
|
||||
pruningPoint, err := bb.newBlockPruningPoint(stagingArea, tempBlockHash)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
coinbase, err := bb.coinbaseManager.ExpectedCoinbaseTransaction(stagingArea, tempBlockHash, coinbaseData, pruningPoint)
|
||||
coinbase, err := bb.coinbaseManager.ExpectedCoinbaseTransaction(stagingArea, tempBlockHash, coinbaseData)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashset"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/pow"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -13,8 +12,11 @@ type blockParentBuilder struct {
|
||||
databaseContext model.DBManager
|
||||
blockHeaderStore model.BlockHeaderStore
|
||||
dagTopologyManager model.DAGTopologyManager
|
||||
parentsManager model.ParentsManager
|
||||
reachabilityDataStore model.ReachabilityDataStore
|
||||
pruningStore model.PruningStore
|
||||
|
||||
genesisHash *externalapi.DomainHash
|
||||
}
|
||||
|
||||
// New creates a new instance of a BlockParentBuilder
|
||||
@@ -22,20 +24,27 @@ func New(
|
||||
databaseContext model.DBManager,
|
||||
blockHeaderStore model.BlockHeaderStore,
|
||||
dagTopologyManager model.DAGTopologyManager,
|
||||
parentsManager model.ParentsManager,
|
||||
|
||||
reachabilityDataStore model.ReachabilityDataStore,
|
||||
pruningStore model.PruningStore,
|
||||
|
||||
genesisHash *externalapi.DomainHash,
|
||||
) model.BlockParentBuilder {
|
||||
return &blockParentBuilder{
|
||||
databaseContext: databaseContext,
|
||||
blockHeaderStore: blockHeaderStore,
|
||||
dagTopologyManager: dagTopologyManager,
|
||||
databaseContext: databaseContext,
|
||||
blockHeaderStore: blockHeaderStore,
|
||||
dagTopologyManager: dagTopologyManager,
|
||||
parentsManager: parentsManager,
|
||||
|
||||
reachabilityDataStore: reachabilityDataStore,
|
||||
pruningStore: pruningStore,
|
||||
genesisHash: genesisHash,
|
||||
}
|
||||
}
|
||||
|
||||
func (bpb *blockParentBuilder) BuildParents(stagingArea *model.StagingArea,
|
||||
directParentHashes []*externalapi.DomainHash) ([]externalapi.BlockLevelParents, error) {
|
||||
daaScore uint64, directParentHashes []*externalapi.DomainHash) ([]externalapi.BlockLevelParents, error) {
|
||||
|
||||
// Late on we'll mutate direct parent hashes, so we first clone it.
|
||||
directParentHashesCopy := make([]*externalapi.DomainHash, len(directParentHashes))
|
||||
@@ -93,7 +102,7 @@ func (bpb *blockParentBuilder) BuildParents(stagingArea *model.StagingArea,
|
||||
// all the block levels they occupy
|
||||
for _, directParentHeader := range directParentHeaders {
|
||||
directParentHash := consensushashing.HeaderHash(directParentHeader)
|
||||
blockLevel := pow.BlockLevel(directParentHeader)
|
||||
blockLevel := directParentHeader.BlockLevel()
|
||||
for i := 0; i <= blockLevel; i++ {
|
||||
if _, exists := candidatesByLevelToReferenceBlocksMap[i]; !exists {
|
||||
candidatesByLevelToReferenceBlocksMap[i] = make(map[externalapi.DomainHash][]*externalapi.DomainHash)
|
||||
@@ -116,7 +125,7 @@ func (bpb *blockParentBuilder) BuildParents(stagingArea *model.StagingArea,
|
||||
}
|
||||
|
||||
for _, directParentHeader := range directParentHeaders {
|
||||
for blockLevel, blockLevelParentsInHeader := range directParentHeader.Parents() {
|
||||
for blockLevel, blockLevelParentsInHeader := range bpb.parentsManager.Parents(directParentHeader) {
|
||||
isEmptyLevel := false
|
||||
if _, exists := candidatesByLevelToReferenceBlocksMap[blockLevel]; !exists {
|
||||
candidatesByLevelToReferenceBlocksMap[blockLevel] = make(map[externalapi.DomainHash][]*externalapi.DomainHash)
|
||||
@@ -145,7 +154,7 @@ func (bpb *blockParentBuilder) BuildParents(stagingArea *model.StagingArea,
|
||||
} else {
|
||||
for childHash, childHeader := range virtualGenesisChildrenHeaders {
|
||||
childHash := childHash // Assign to a new pointer to avoid `range` pointer reuse
|
||||
if childHeader.ParentsAtLevel(blockLevel).Contains(parent) {
|
||||
if bpb.parentsManager.ParentsAtLevel(childHeader, blockLevel).Contains(parent) {
|
||||
referenceBlocks = append(referenceBlocks, &childHash)
|
||||
}
|
||||
}
|
||||
@@ -203,14 +212,21 @@ func (bpb *blockParentBuilder) BuildParents(stagingArea *model.StagingArea,
|
||||
}
|
||||
}
|
||||
|
||||
parents := make([]externalapi.BlockLevelParents, len(candidatesByLevelToReferenceBlocksMap))
|
||||
parents := make([]externalapi.BlockLevelParents, 0, len(candidatesByLevelToReferenceBlocksMap))
|
||||
for blockLevel := 0; blockLevel < len(candidatesByLevelToReferenceBlocksMap); blockLevel++ {
|
||||
if blockLevel > 0 {
|
||||
if _, ok := candidatesByLevelToReferenceBlocksMap[blockLevel][*bpb.genesisHash]; ok && len(candidatesByLevelToReferenceBlocksMap[blockLevel]) == 1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
levelBlocks := make(externalapi.BlockLevelParents, 0, len(candidatesByLevelToReferenceBlocksMap[blockLevel]))
|
||||
for block := range candidatesByLevelToReferenceBlocksMap[blockLevel] {
|
||||
block := block // Assign to a new pointer to avoid `range` pointer reuse
|
||||
levelBlocks = append(levelBlocks, &block)
|
||||
}
|
||||
parents[blockLevel] = levelBlocks
|
||||
|
||||
parents = append(parents, levelBlocks)
|
||||
}
|
||||
return parents, nil
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ func New(
|
||||
|
||||
// ValidateAndInsertBlock validates the given block and, if valid, applies it
|
||||
// to the current state
|
||||
func (bp *blockProcessor) ValidateAndInsertBlock(block *externalapi.DomainBlock, shouldValidateAgainstUTXO bool) (*externalapi.BlockInsertionResult, error) {
|
||||
func (bp *blockProcessor) ValidateAndInsertBlock(block *externalapi.DomainBlock, shouldValidateAgainstUTXO bool) (*externalapi.VirtualChangeSet, error) {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "ValidateAndInsertBlock")
|
||||
defer onEnd()
|
||||
|
||||
@@ -159,7 +159,7 @@ func (bp *blockProcessor) ValidateAndInsertImportedPruningPoint(newPruningPoint
|
||||
return bp.validateAndInsertImportedPruningPoint(stagingArea, newPruningPoint)
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) ValidateAndInsertBlockWithTrustedData(block *externalapi.BlockWithTrustedData, shouldValidateAgainstUTXO bool) (*externalapi.BlockInsertionResult, error) {
|
||||
func (bp *blockProcessor) ValidateAndInsertBlockWithTrustedData(block *externalapi.BlockWithTrustedData, shouldValidateAgainstUTXO bool) (*externalapi.VirtualChangeSet, error) {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "ValidateAndInsertBlockWithTrustedData")
|
||||
defer onEnd()
|
||||
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
package blockprocessor
|
||||
|
||||
import (
|
||||
// we need to embed the utxoset of mainnet genesis here
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||
|
||||
"github.com/kaspanet/kaspad/util/staging"
|
||||
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
|
||||
"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/multiset"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||
"github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
"github.com/kaspanet/kaspad/util/staging"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -76,7 +77,7 @@ func (bp *blockProcessor) updateVirtualAcceptanceDataAfterImportingPruningPoint(
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) validateAndInsertBlock(stagingArea *model.StagingArea, block *externalapi.DomainBlock,
|
||||
isPruningPoint bool, shouldValidateAgainstUTXO bool, isBlockWithTrustedData bool) (*externalapi.BlockInsertionResult, error) {
|
||||
isPruningPoint bool, shouldValidateAgainstUTXO bool, isBlockWithTrustedData bool) (*externalapi.VirtualChangeSet, error) {
|
||||
|
||||
blockHash := consensushashing.HeaderHash(block.Header)
|
||||
err := bp.validateBlock(stagingArea, block, isBlockWithTrustedData)
|
||||
@@ -102,11 +103,33 @@ func (bp *blockProcessor) validateAndInsertBlock(stagingArea *model.StagingArea,
|
||||
}
|
||||
}
|
||||
|
||||
err = bp.headerTipsManager.AddHeaderTip(stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
shouldAddHeaderSelectedTip := false
|
||||
if !hasHeaderSelectedTip {
|
||||
shouldAddHeaderSelectedTip = true
|
||||
} else {
|
||||
pruningPoint, err := bp.pruningStore.PruningPoint(bp.databaseContext, stagingArea)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
isInSelectedChainOfPruningPoint, err := bp.dagTopologyManager.IsInSelectedParentChainOf(stagingArea, pruningPoint, blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Don't set blocks in the anticone of the pruning point as header selected tip.
|
||||
shouldAddHeaderSelectedTip = isInSelectedChainOfPruningPoint
|
||||
}
|
||||
|
||||
if shouldAddHeaderSelectedTip {
|
||||
// Don't set blocks in the anticone of the pruning point as header selected tip.
|
||||
err = bp.headerTipsManager.AddHeaderTip(stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
bp.loadUTXODataForGenesis(stagingArea, block)
|
||||
var selectedParentChainChanges *externalapi.SelectedChainPath
|
||||
var virtualUTXODiff externalapi.UTXODiff
|
||||
var reversalData *model.UTXODiffReversalData
|
||||
@@ -187,13 +210,34 @@ func (bp *blockProcessor) validateAndInsertBlock(stagingArea *model.StagingArea,
|
||||
|
||||
bp.blockLogger.LogBlock(block)
|
||||
|
||||
return &externalapi.BlockInsertionResult{
|
||||
return &externalapi.VirtualChangeSet{
|
||||
VirtualSelectedParentChainChanges: selectedParentChainChanges,
|
||||
VirtualUTXODiff: virtualUTXODiff,
|
||||
VirtualParents: virtualParents,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) loadUTXODataForGenesis(stagingArea *model.StagingArea, block *externalapi.DomainBlock) {
|
||||
isGenesis := len(block.Header.DirectParents()) == 0
|
||||
if !isGenesis {
|
||||
return
|
||||
}
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
// Note: The applied UTXO set and multiset do not satisfy the UTXO commitment
|
||||
// of Mainnet's genesis. This is why any block that will be built on top of genesis
|
||||
// will have a wrong UTXO commitment as well, and will not be able to get to a consensus
|
||||
// with the rest of the network.
|
||||
// This is why getting direct blocks on top of genesis is forbidden, and the only way to
|
||||
// get a newer state for a node with genesis only is by requesting a proof for a recent
|
||||
// pruning point.
|
||||
// The actual UTXO set that fits Mainnet's genesis' UTXO commitment was removed from the codebase in order
|
||||
// to make reduce the consensus initialization time and the compiled binary size, but can be still
|
||||
// found here for anyone to verify: https://github.com/kaspanet/kaspad/blob/dbf18d8052f000ba0079be9e79b2d6f5a98b74ca/domain/consensus/processes/blockprocessor/resources/utxos.gz
|
||||
bp.consensusStateStore.StageVirtualUTXODiff(stagingArea, utxo.NewUTXODiff())
|
||||
bp.utxoDiffStore.Stage(stagingArea, blockHash, utxo.NewUTXODiff(), nil)
|
||||
bp.multisetStore.Stage(stagingArea, blockHash, multiset.New())
|
||||
}
|
||||
|
||||
func isHeaderOnlyBlock(block *externalapi.DomainBlock) bool {
|
||||
return len(block.Transactions) == 0
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
func (bp *blockProcessor) validateAndInsertBlockWithTrustedData(stagingArea *model.StagingArea,
|
||||
block *externalapi.BlockWithTrustedData, validateUTXO bool) (*externalapi.BlockInsertionResult, error) {
|
||||
block *externalapi.BlockWithTrustedData, validateUTXO bool) (*externalapi.VirtualChangeSet, error) {
|
||||
|
||||
blockHash := consensushashing.BlockHash(block.Block)
|
||||
for i, daaBlock := range block.DAAWindow {
|
||||
|
||||
@@ -87,13 +87,18 @@ func TestValidateAndInsertImportedPruningPoint(t *testing.T) {
|
||||
t.Fatalf("PruningPointHeaders: %+v", err)
|
||||
}
|
||||
|
||||
pruningPointAndItsAnticoneWithTrustedData, err := tcSyncer.PruningPointAndItsAnticoneWithTrustedData()
|
||||
pruningPointAndItsAnticone, err := tcSyncer.PruningPointAndItsAnticone()
|
||||
if err != nil {
|
||||
t.Fatalf("PruningPointAndItsAnticoneWithTrustedData: %+v", err)
|
||||
t.Fatalf("PruningPointAndItsAnticone: %+v", err)
|
||||
}
|
||||
|
||||
for _, blockWithTrustedData := range pruningPointAndItsAnticoneWithTrustedData {
|
||||
_, err := synceeStaging.ValidateAndInsertBlockWithTrustedData(blockWithTrustedData, false)
|
||||
for _, blockHash := range pruningPointAndItsAnticone {
|
||||
blockWithTrustedData, err := tcSyncer.BlockWithTrustedData(blockHash)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = synceeStaging.ValidateAndInsertBlockWithTrustedData(blockWithTrustedData, false)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateAndInsertBlockWithTrustedData: %+v", err)
|
||||
}
|
||||
@@ -135,10 +140,21 @@ func TestValidateAndInsertImportedPruningPoint(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
pruningPointUTXOs, err := tcSyncer.GetPruningPointUTXOs(pruningPoint, nil, 1000)
|
||||
if err != nil {
|
||||
t.Fatalf("GetPruningPointUTXOs: %+v", err)
|
||||
var fromOutpoint *externalapi.DomainOutpoint
|
||||
var pruningPointUTXOs []*externalapi.OutpointAndUTXOEntryPair
|
||||
const step = 100_000
|
||||
for {
|
||||
outpointAndUTXOEntryPairs, err := tcSyncer.GetPruningPointUTXOs(pruningPoint, fromOutpoint, step)
|
||||
if err != nil {
|
||||
t.Fatalf("GetPruningPointUTXOs: %+v", err)
|
||||
}
|
||||
fromOutpoint = outpointAndUTXOEntryPairs[len(outpointAndUTXOEntryPairs)-1].Outpoint
|
||||
pruningPointUTXOs = append(pruningPointUTXOs, outpointAndUTXOEntryPairs...)
|
||||
if len(outpointAndUTXOEntryPairs) < step {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
err = synceeStaging.AppendImportedPruningPointUTXOs(pruningPointUTXOs)
|
||||
if err != nil {
|
||||
t.Fatalf("AppendImportedPruningPointUTXOs: %+v", err)
|
||||
@@ -502,7 +518,7 @@ func TestGetPruningPointUTXOs(t *testing.T) {
|
||||
|
||||
// Get pruning point UTXOs in a loop
|
||||
var allOutpointAndUTXOEntryPairs []*externalapi.OutpointAndUTXOEntryPair
|
||||
step := 100
|
||||
const step = 100_000
|
||||
var fromOutpoint *externalapi.DomainOutpoint
|
||||
for {
|
||||
outpointAndUTXOEntryPairs, err := testConsensus.GetPruningPointUTXOs(pruningPoint, fromOutpoint, step)
|
||||
@@ -517,11 +533,12 @@ func TestGetPruningPointUTXOs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
expected := len(outputs) + 1
|
||||
// Make sure the length of the UTXOs is exactly spendingTransaction.Outputs + 1 coinbase
|
||||
// output (includingBlock's coinbase)
|
||||
if len(allOutpointAndUTXOEntryPairs) != len(outputs)+1 {
|
||||
if len(allOutpointAndUTXOEntryPairs) != expected {
|
||||
t.Fatalf("Returned an unexpected amount of UTXOs. "+
|
||||
"Want: %d, got: %d", len(outputs)+2, len(allOutpointAndUTXOEntryPairs))
|
||||
"Want: %d, got: %d", expected, len(allOutpointAndUTXOEntryPairs))
|
||||
}
|
||||
|
||||
// Make sure all spendingTransaction.Outputs are in the returned UTXOs
|
||||
|
||||
@@ -178,7 +178,7 @@ func (v *blockValidator) checkCoinbaseSubsidy(
|
||||
return err
|
||||
}
|
||||
|
||||
expectedSubsidy, err := v.coinbaseManager.CalcBlockSubsidy(stagingArea, blockHash, block.Header.PruningPoint())
|
||||
expectedSubsidy, err := v.coinbaseManager.CalcBlockSubsidy(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -220,7 +220,9 @@ func (v *blockValidator) validateGasLimit(block *externalapi.DomainBlock) error
|
||||
|
||||
func (v *blockValidator) checkBlockMass(block *externalapi.DomainBlock) error {
|
||||
mass := uint64(0)
|
||||
mass += v.headerEstimatedSerializedSize(block.Header)
|
||||
if !v.ignoreHeaderMass {
|
||||
mass += v.headerEstimatedSerializedSize(block.Header)
|
||||
}
|
||||
|
||||
for _, transaction := range block.Transactions {
|
||||
v.transactionValidator.PopulateMass(transaction)
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"bytes"
|
||||
"math"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
@@ -21,6 +23,31 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func TestBlockValidator_ValidateBodyInIsolation(t *testing.T) {
|
||||
tests := []func(t *testing.T, tc testapi.TestConsensus, cfg *consensus.Config){
|
||||
CheckBlockSanity,
|
||||
CheckBlockHashMerkleRoot,
|
||||
BlockMass,
|
||||
CheckBlockDuplicateTransactions,
|
||||
CheckBlockContainsOnlyOneCoinbase,
|
||||
CheckBlockDoubleSpends,
|
||||
CheckFirstBlockTransactionIsCoinbase,
|
||||
}
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
tc, teardown, err := consensus.NewFactory().NewTestConsensus(consensusConfig, "TestChainedTransactions")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
for _, test := range tests {
|
||||
testName := runtime.FuncForPC(reflect.ValueOf(test).Pointer()).Name()
|
||||
t.Run(testName, func(t *testing.T) {
|
||||
test(t, tc, consensusConfig)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestChainedTransactions(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
consensusConfig.BlockCoinbaseMaturity = 0
|
||||
@@ -89,47 +116,39 @@ func TestChainedTransactions(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// TestCheckBlockSanity tests the CheckBlockSanity function to ensure it works
|
||||
// CheckBlockSanity tests the CheckBlockSanity function to ensure it works
|
||||
// as expected.
|
||||
func TestCheckBlockSanity(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckBlockSanity")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
blockHash := consensushashing.BlockHash(&exampleValidBlock)
|
||||
if len(exampleValidBlock.Transactions) < 3 {
|
||||
t.Fatalf("Too few transactions in block, expect at least 3, got %v", len(exampleValidBlock.Transactions))
|
||||
}
|
||||
func CheckBlockSanity(t *testing.T, tc testapi.TestConsensus, _ *consensus.Config) {
|
||||
blockHash := consensushashing.BlockHash(&exampleValidBlock)
|
||||
if len(exampleValidBlock.Transactions) < 3 {
|
||||
t.Fatalf("Too few transactions in block, expect at least 3, got %v", len(exampleValidBlock.Transactions))
|
||||
}
|
||||
|
||||
stagingArea := model.NewStagingArea()
|
||||
stagingArea := model.NewStagingArea()
|
||||
|
||||
tc.BlockStore().Stage(stagingArea, blockHash, &exampleValidBlock)
|
||||
tc.BlockStore().Stage(stagingArea, blockHash, &exampleValidBlock)
|
||||
|
||||
err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed validating block in isolation: %v", err)
|
||||
}
|
||||
err := tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed validating block in isolation: %v", err)
|
||||
}
|
||||
|
||||
// Test with block with wrong transactions sorting order
|
||||
blockHash = consensushashing.BlockHash(&blockWithWrongTxOrder)
|
||||
tc.BlockStore().Stage(stagingArea, blockHash, &blockWithWrongTxOrder)
|
||||
err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash)
|
||||
if !errors.Is(err, ruleerrors.ErrTransactionsNotSorted) {
|
||||
t.Errorf("CheckBlockSanity: Expected ErrTransactionsNotSorted error, instead got %v", err)
|
||||
}
|
||||
// Test with block with wrong transactions sorting order
|
||||
blockHash = consensushashing.BlockHash(&blockWithWrongTxOrder)
|
||||
tc.BlockStore().Stage(stagingArea, blockHash, &blockWithWrongTxOrder)
|
||||
err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash)
|
||||
if !errors.Is(err, ruleerrors.ErrTransactionsNotSorted) {
|
||||
t.Errorf("CheckBlockSanity: Expected ErrTransactionsNotSorted error, instead got %v", err)
|
||||
}
|
||||
|
||||
// Test a block with invalid parents order
|
||||
// We no longer require blocks to have ordered parents
|
||||
blockHash = consensushashing.BlockHash(&unOrderedParentsBlock)
|
||||
tc.BlockStore().Stage(stagingArea, blockHash, &unOrderedParentsBlock)
|
||||
err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash)
|
||||
if err != nil {
|
||||
t.Errorf("CheckBlockSanity: Expected block to be be body in isolation valid, got error instead: %v", err)
|
||||
}
|
||||
})
|
||||
// Test a block with invalid parents order
|
||||
// We no longer require blocks to have ordered parents
|
||||
blockHash = consensushashing.BlockHash(&unOrderedParentsBlock)
|
||||
tc.BlockStore().Stage(stagingArea, blockHash, &unOrderedParentsBlock)
|
||||
err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash)
|
||||
if err != nil {
|
||||
t.Errorf("CheckBlockSanity: Expected block to be be body in isolation valid, got error instead: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
var unOrderedParentsBlock = externalapi.DomainBlock{
|
||||
@@ -1025,59 +1044,41 @@ var blockWithWrongTxOrder = externalapi.DomainBlock{
|
||||
},
|
||||
}
|
||||
|
||||
func TestCheckBlockHashMerkleRoot(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckBlockHashMerkleRoot")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tc: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
func CheckBlockHashMerkleRoot(t *testing.T, tc testapi.TestConsensus, consensusConfig *consensus.Config) {
|
||||
block, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("BuildBlockWithParents: %+v", err)
|
||||
}
|
||||
blockWithInvalidMerkleRoot := block.Clone()
|
||||
blockWithInvalidMerkleRoot.Transactions[0].Version += 1
|
||||
|
||||
block, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("BuildBlockWithParents: %+v", err)
|
||||
}
|
||||
blockWithInvalidMerkleRoot := block.Clone()
|
||||
blockWithInvalidMerkleRoot.Transactions[0].Version += 1
|
||||
_, err = tc.ValidateAndInsertBlock(blockWithInvalidMerkleRoot, true)
|
||||
if !errors.Is(err, ruleerrors.ErrBadMerkleRoot) {
|
||||
t.Fatalf("Unexpected error: %+v", err)
|
||||
}
|
||||
|
||||
_, err = tc.ValidateAndInsertBlock(blockWithInvalidMerkleRoot, true)
|
||||
if !errors.Is(err, ruleerrors.ErrBadMerkleRoot) {
|
||||
t.Fatalf("Unexpected error: %+v", err)
|
||||
}
|
||||
|
||||
// Check that a block with invalid merkle root is not marked as invalid
|
||||
// and can be re-added with the right transactions.
|
||||
_, err = tc.ValidateAndInsertBlock(block, true)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateAndInsertBlock: %+v", err)
|
||||
}
|
||||
})
|
||||
// Check that a block with invalid merkle root is not marked as invalid
|
||||
// and can be re-added with the right transactions.
|
||||
_, err = tc.ValidateAndInsertBlock(block, true)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateAndInsertBlock: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockMass(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestBlockMass")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tc: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
func BlockMass(t *testing.T, tc testapi.TestConsensus, consensusConfig *consensus.Config) {
|
||||
block, _, err := initBlockWithInvalidBlockMass(consensusConfig, tc)
|
||||
if err != nil {
|
||||
t.Fatalf("Error BuildBlockWithParents : %+v", err)
|
||||
}
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
stagingArea := model.NewStagingArea()
|
||||
tc.BlockStore().Stage(stagingArea, blockHash, block)
|
||||
|
||||
block, _, err := initBlockWithInvalidBlockMass(consensusConfig, tc)
|
||||
if err != nil {
|
||||
t.Fatalf("Error BuildBlockWithParents : %+v", err)
|
||||
}
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
stagingArea := model.NewStagingArea()
|
||||
tc.BlockStore().Stage(stagingArea, blockHash, block)
|
||||
|
||||
err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash)
|
||||
if err == nil || !errors.Is(err, ruleerrors.ErrBlockMassTooHigh) {
|
||||
t.Fatalf("ValidateBodyInIsolationTest: TestBlockMass:"+
|
||||
" Unexpected error: Expected to: %v, but got : %v", ruleerrors.ErrBlockMassTooHigh, err)
|
||||
}
|
||||
})
|
||||
err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash)
|
||||
if err == nil || !errors.Is(err, ruleerrors.ErrBlockMassTooHigh) {
|
||||
t.Fatalf("ValidateBodyInIsolationTest: TestBlockMass:"+
|
||||
" Unexpected error: Expected to: %v, but got : %v", ruleerrors.ErrBlockMassTooHigh, err)
|
||||
}
|
||||
}
|
||||
|
||||
func initBlockWithInvalidBlockMass(consensusConfig *consensus.Config, tc testapi.TestConsensus) (*externalapi.DomainBlock, externalapi.UTXODiff, error) {
|
||||
@@ -1113,30 +1114,20 @@ func initBlockWithInvalidBlockMass(consensusConfig *consensus.Config, tc testapi
|
||||
return tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, &emptyCoinbase, []*externalapi.DomainTransaction{tx})
|
||||
}
|
||||
|
||||
func TestCheckBlockDuplicateTransactions(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
func CheckBlockDuplicateTransactions(t *testing.T, tc testapi.TestConsensus, consensusConfig *consensus.Config) {
|
||||
block, _, err := initBlockWithDuplicateTransaction(consensusConfig, tc)
|
||||
if err != nil {
|
||||
t.Fatalf("Error BuildBlockWithParents : %+v", err)
|
||||
}
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
stagingArea := model.NewStagingArea()
|
||||
tc.BlockStore().Stage(stagingArea, blockHash, block)
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckBlockDuplicateTransactions")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tc: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
block, _, err := initBlockWithDuplicateTransaction(consensusConfig, tc)
|
||||
if err != nil {
|
||||
t.Fatalf("Error BuildBlockWithParents : %+v", err)
|
||||
}
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
stagingArea := model.NewStagingArea()
|
||||
tc.BlockStore().Stage(stagingArea, blockHash, block)
|
||||
|
||||
err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash)
|
||||
if err == nil || !errors.Is(err, ruleerrors.ErrDuplicateTx) {
|
||||
t.Fatalf("ValidateBodyInIsolationTest: TestCheckBlockDuplicateTransactions:"+
|
||||
" Unexpected error: Expected to: %v, but got : %v", ruleerrors.ErrDuplicateTx, err)
|
||||
}
|
||||
})
|
||||
err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash)
|
||||
if err == nil || !errors.Is(err, ruleerrors.ErrDuplicateTx) {
|
||||
t.Fatalf("ValidateBodyInIsolationTest: TestCheckBlockDuplicateTransactions:"+
|
||||
" Unexpected error: Expected to: %v, but got : %v", ruleerrors.ErrDuplicateTx, err)
|
||||
}
|
||||
}
|
||||
|
||||
func initBlockWithDuplicateTransaction(consensusConfig *consensus.Config, tc testapi.TestConsensus) (*externalapi.DomainBlock, externalapi.UTXODiff, error) {
|
||||
@@ -1170,30 +1161,20 @@ func initBlockWithDuplicateTransaction(consensusConfig *consensus.Config, tc tes
|
||||
return tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, &emptyCoinbase, []*externalapi.DomainTransaction{tx, tx})
|
||||
}
|
||||
|
||||
func TestCheckBlockContainsOnlyOneCoinbase(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
func CheckBlockContainsOnlyOneCoinbase(t *testing.T, tc testapi.TestConsensus, consensusConfig *consensus.Config) {
|
||||
block, _, err := initBlockWithMoreThanOneCoinbase(consensusConfig, tc)
|
||||
if err != nil {
|
||||
t.Fatalf("Error BuildBlockWithParents : %+v", err)
|
||||
}
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
stagingArea := model.NewStagingArea()
|
||||
tc.BlockStore().Stage(stagingArea, blockHash, block)
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckBlockContainsOnlyOneCoinbase")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tc: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
block, _, err := initBlockWithMoreThanOneCoinbase(consensusConfig, tc)
|
||||
if err != nil {
|
||||
t.Fatalf("Error BuildBlockWithParents : %+v", err)
|
||||
}
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
stagingArea := model.NewStagingArea()
|
||||
tc.BlockStore().Stage(stagingArea, blockHash, block)
|
||||
|
||||
err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash)
|
||||
if err == nil || !errors.Is(err, ruleerrors.ErrMultipleCoinbases) {
|
||||
t.Fatalf("ValidateBodyInIsolationTest: TestCheckBlockContainsOnlyOneCoinbase:"+
|
||||
" Unexpected error: Expected to: %v, but got : %v", ruleerrors.ErrMultipleCoinbases, err)
|
||||
}
|
||||
})
|
||||
err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash)
|
||||
if err == nil || !errors.Is(err, ruleerrors.ErrMultipleCoinbases) {
|
||||
t.Fatalf("ValidateBodyInIsolationTest: TestCheckBlockContainsOnlyOneCoinbase:"+
|
||||
" Unexpected error: Expected to: %v, but got : %v", ruleerrors.ErrMultipleCoinbases, err)
|
||||
}
|
||||
}
|
||||
|
||||
func initBlockWithMoreThanOneCoinbase(consensusConfig *consensus.Config, tc testapi.TestConsensus) (*externalapi.DomainBlock, externalapi.UTXODiff, error) {
|
||||
@@ -1227,30 +1208,20 @@ func initBlockWithMoreThanOneCoinbase(consensusConfig *consensus.Config, tc test
|
||||
return tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, &emptyCoinbase, []*externalapi.DomainTransaction{tx})
|
||||
}
|
||||
|
||||
func TestCheckBlockDoubleSpends(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
func CheckBlockDoubleSpends(t *testing.T, tc testapi.TestConsensus, consensusConfig *consensus.Config) {
|
||||
block, _, err := initBlockWithDoubleSpends(consensusConfig, tc)
|
||||
if err != nil {
|
||||
t.Fatalf("Error BuildBlockWithParents : %+v", err)
|
||||
}
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
stagingArea := model.NewStagingArea()
|
||||
tc.BlockStore().Stage(stagingArea, blockHash, block)
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckBlockDoubleSpends")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tc: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
block, _, err := initBlockWithDoubleSpends(consensusConfig, tc)
|
||||
if err != nil {
|
||||
t.Fatalf("Error BuildBlockWithParents : %+v", err)
|
||||
}
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
stagingArea := model.NewStagingArea()
|
||||
tc.BlockStore().Stage(stagingArea, blockHash, block)
|
||||
|
||||
err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash)
|
||||
if err == nil || !errors.Is(err, ruleerrors.ErrDoubleSpendInSameBlock) {
|
||||
t.Fatalf("ValidateBodyInIsolationTest: TestCheckBlockDoubleSpends:"+
|
||||
" Unexpected error: Expected to: %v, but got : %v", ruleerrors.ErrDoubleSpendInSameBlock, err)
|
||||
}
|
||||
})
|
||||
err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash)
|
||||
if err == nil || !errors.Is(err, ruleerrors.ErrDoubleSpendInSameBlock) {
|
||||
t.Fatalf("ValidateBodyInIsolationTest: TestCheckBlockDoubleSpends:"+
|
||||
" Unexpected error: Expected to: %v, but got : %v", ruleerrors.ErrDoubleSpendInSameBlock, err)
|
||||
}
|
||||
}
|
||||
|
||||
func initBlockWithDoubleSpends(consensusConfig *consensus.Config, tc testapi.TestConsensus) (*externalapi.DomainBlock, externalapi.UTXODiff, error) {
|
||||
@@ -1303,27 +1274,18 @@ func initBlockWithDoubleSpends(consensusConfig *consensus.Config, tc testapi.Tes
|
||||
&emptyCoinbase, []*externalapi.DomainTransaction{tx, txSameOutpoint})
|
||||
}
|
||||
|
||||
func TestCheckFirstBlockTransactionIsCoinbase(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
func CheckFirstBlockTransactionIsCoinbase(t *testing.T, tc testapi.TestConsensus, consensusConfig *consensus.Config) {
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckFirstBlockTransactionIsCoinbase")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tc: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
block := initBlockWithFirstTransactionDifferentThanCoinbase(consensusConfig)
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
stagingArea := model.NewStagingArea()
|
||||
tc.BlockStore().Stage(stagingArea, blockHash, block)
|
||||
|
||||
block := initBlockWithFirstTransactionDifferentThanCoinbase(consensusConfig)
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
stagingArea := model.NewStagingArea()
|
||||
tc.BlockStore().Stage(stagingArea, blockHash, block)
|
||||
|
||||
err = tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash)
|
||||
if err == nil || !errors.Is(err, ruleerrors.ErrFirstTxNotCoinbase) {
|
||||
t.Fatalf("ValidateBodyInIsolationTest: TestCheckFirstBlockTransactionIsCoinbase:"+
|
||||
" Unexpected error: Expected to: %v, but got : %v", ruleerrors.ErrFirstTxNotCoinbase, err)
|
||||
}
|
||||
})
|
||||
err := tc.BlockValidator().ValidateBodyInIsolation(stagingArea, blockHash)
|
||||
if err == nil || !errors.Is(err, ruleerrors.ErrFirstTxNotCoinbase) {
|
||||
t.Fatalf("ValidateBodyInIsolationTest: TestCheckFirstBlockTransactionIsCoinbase:"+
|
||||
" Unexpected error: Expected to: %v, but got : %v", ruleerrors.ErrFirstTxNotCoinbase, err)
|
||||
}
|
||||
}
|
||||
|
||||
func initBlockWithFirstTransactionDifferentThanCoinbase(consensusConfig *consensus.Config) *externalapi.DomainBlock {
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"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/pow"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -63,7 +62,7 @@ func (v *blockValidator) ValidateHeaderInContext(stagingArea *model.StagingArea,
|
||||
return err
|
||||
}
|
||||
if !hasReachabilityData {
|
||||
blockLevel := pow.BlockLevel(header)
|
||||
blockLevel := header.BlockLevel()
|
||||
for i := 0; i <= blockLevel; i++ {
|
||||
err = v.reachabilityManagers[i].AddBlock(stagingArea, blockHash)
|
||||
if err != nil {
|
||||
@@ -195,7 +194,7 @@ func (v *blockValidator) checkMergeSizeLimit(stagingArea *model.StagingArea, has
|
||||
}
|
||||
|
||||
func (v *blockValidator) checkIndirectParents(stagingArea *model.StagingArea, header externalapi.BlockHeader) error {
|
||||
expectedParents, err := v.blockParentBuilder.BuildParents(stagingArea, header.DirectParents())
|
||||
expectedParents, err := v.blockParentBuilder.BuildParents(stagingArea, header.DAAScore(), header.DirectParents())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -67,8 +67,8 @@ func TestValidateMedianTime(t *testing.T) {
|
||||
|
||||
blockTime := tip.Header.TimeInMilliseconds()
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
blockTime += 1000
|
||||
for i := 0; i < 10; i++ {
|
||||
blockTime += 100
|
||||
_, tipHash = addBlock(blockTime, []*externalapi.DomainHash{tipHash}, nil)
|
||||
}
|
||||
|
||||
@@ -163,16 +163,17 @@ func TestCheckParentsIncest(t *testing.T) {
|
||||
|
||||
func TestCheckMergeSizeLimit(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
consensusConfig.MergeSetSizeLimit = 2 * uint64(consensusConfig.K)
|
||||
consensusConfig.MergeSetSizeLimit = 5
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckParentsIncest")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckMergeSizeLimit")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
chain1TipHash := consensusConfig.GenesisHash
|
||||
for i := uint64(0); i < consensusConfig.MergeSetSizeLimit+2; i++ {
|
||||
// We add a chain larger by one than chain2 below, to make this one the selected chain
|
||||
for i := uint64(0); i < consensusConfig.MergeSetSizeLimit+1; i++ {
|
||||
chain1TipHash, _, err = tc.AddBlock([]*externalapi.DomainHash{chain1TipHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
@@ -180,7 +181,9 @@ func TestCheckMergeSizeLimit(t *testing.T) {
|
||||
}
|
||||
|
||||
chain2TipHash := consensusConfig.GenesisHash
|
||||
for i := uint64(0); i < consensusConfig.MergeSetSizeLimit+1; i++ {
|
||||
// We add a merge set of size exactly MergeSetSizeLimit (to violate the limit),
|
||||
// since selected parent is also counted
|
||||
for i := uint64(0); i < consensusConfig.MergeSetSizeLimit; i++ {
|
||||
chain2TipHash, _, err = tc.AddBlock([]*externalapi.DomainHash{chain2TipHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
@@ -193,3 +196,56 @@ func TestCheckMergeSizeLimit(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestVirtualSelectionViolatingMergeSizeLimit(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
consensusConfig.MergeSetSizeLimit = 2 * uint64(consensusConfig.K)
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestVirtualSelectionViolatingMergeSizeLimit")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
chain1TipHash := consensusConfig.GenesisHash
|
||||
// We add a chain larger than chain2 below, to make this one the selected chain
|
||||
for i := uint64(0); i < consensusConfig.MergeSetSizeLimit; i++ {
|
||||
chain1TipHash, _, err = tc.AddBlock([]*externalapi.DomainHash{chain1TipHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
chain2TipHash := consensusConfig.GenesisHash
|
||||
// We add a merge set of size exactly MergeSetSizeLimit-1 (to still not violate the limit)
|
||||
for i := uint64(0); i < consensusConfig.MergeSetSizeLimit-1; i++ {
|
||||
chain2TipHash, _, err = tc.AddBlock([]*externalapi.DomainHash{chain2TipHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// We now add a single block over genesis which is expected to exceed the limit
|
||||
_, _, err = tc.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
|
||||
stagingArea := model.NewStagingArea()
|
||||
virtualSelectedParent, err := tc.GetVirtualSelectedParent()
|
||||
if err != nil {
|
||||
t.Fatalf("GetVirtualSelectedParent: %+v", err)
|
||||
}
|
||||
selectedParentAnticone, err := tc.DAGTraversalManager().AnticoneFromVirtualPOV(stagingArea, virtualSelectedParent)
|
||||
if err != nil {
|
||||
t.Fatalf("AnticoneFromVirtualPOV: %+v", err)
|
||||
}
|
||||
|
||||
// Test if Virtual's mergeset is too large
|
||||
// Note: the selected parent itself is also counted in the mergeset limit
|
||||
if len(selectedParentAnticone)+1 > (int)(consensusConfig.MergeSetSizeLimit) {
|
||||
t.Fatalf("Virtual's mergset size (%d) exeeds merge set limit (%d)",
|
||||
len(selectedParentAnticone)+1, consensusConfig.MergeSetSizeLimit)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package blockvalidator_test
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
@@ -13,73 +16,74 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func TestCheckParentsLimit(t *testing.T) {
|
||||
func TestBlockValidator_ValidateHeaderInIsolation(t *testing.T) {
|
||||
tests := []func(t *testing.T, tc testapi.TestConsensus, cfg *consensus.Config){
|
||||
CheckParentsLimit,
|
||||
CheckBlockVersion,
|
||||
CheckBlockTimestampInIsolation,
|
||||
}
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckParentsLimit")
|
||||
tc, teardown, err := consensus.NewFactory().NewTestConsensus(consensusConfig, "TestBlockValidator_ValidateHeaderInIsolation")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
for i := externalapi.KType(0); i < consensusConfig.MaxBlockParents+1; i++ {
|
||||
_, _, err = tc.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
tips, err := tc.Tips()
|
||||
if err != nil {
|
||||
t.Fatalf("Tips: %+v", err)
|
||||
}
|
||||
|
||||
_, _, err = tc.AddBlock(tips, nil, nil)
|
||||
if !errors.Is(err, ruleerrors.ErrTooManyParents) {
|
||||
t.Fatalf("Unexpected error: %+v", err)
|
||||
for _, test := range tests {
|
||||
testName := runtime.FuncForPC(reflect.ValueOf(test).Pointer()).Name()
|
||||
t.Run(testName, func(t *testing.T) {
|
||||
test(t, tc, consensusConfig)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCheckBlockVersion(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckBlockVersion")
|
||||
func CheckParentsLimit(t *testing.T, tc testapi.TestConsensus, consensusConfig *consensus.Config) {
|
||||
for i := externalapi.KType(0); i < consensusConfig.MaxBlockParents+1; i++ {
|
||||
_, _, err := tc.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
}
|
||||
|
||||
block, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("BuildBlockWithParents: %+v", err)
|
||||
}
|
||||
tips, err := tc.Tips()
|
||||
if err != nil {
|
||||
t.Fatalf("Tips: %+v", err)
|
||||
}
|
||||
|
||||
block.Header = blockheader.NewImmutableBlockHeader(
|
||||
constants.MaxBlockVersion+1,
|
||||
block.Header.Parents(),
|
||||
block.Header.HashMerkleRoot(),
|
||||
block.Header.AcceptedIDMerkleRoot(),
|
||||
block.Header.UTXOCommitment(),
|
||||
block.Header.TimeInMilliseconds(),
|
||||
block.Header.Bits(),
|
||||
block.Header.Nonce(),
|
||||
block.Header.DAAScore(),
|
||||
block.Header.BlueScore(),
|
||||
block.Header.BlueWork(),
|
||||
block.Header.PruningPoint(),
|
||||
)
|
||||
|
||||
_, err = tc.ValidateAndInsertBlock(block, true)
|
||||
if !errors.Is(err, ruleerrors.ErrBlockVersionIsUnknown) {
|
||||
t.Fatalf("Unexpected error: %+v", err)
|
||||
}
|
||||
})
|
||||
_, _, err = tc.AddBlock(tips, nil, nil)
|
||||
if !errors.Is(err, ruleerrors.ErrTooManyParents) {
|
||||
t.Fatalf("Unexpected error: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckBlockTimestampInIsolation(t *testing.T) {
|
||||
func CheckBlockVersion(t *testing.T, tc testapi.TestConsensus, consensusConfig *consensus.Config) {
|
||||
block, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("BuildBlockWithParents: %+v", err)
|
||||
}
|
||||
|
||||
block.Header = blockheader.NewImmutableBlockHeader(
|
||||
constants.MaxBlockVersion+1,
|
||||
block.Header.Parents(),
|
||||
block.Header.HashMerkleRoot(),
|
||||
block.Header.AcceptedIDMerkleRoot(),
|
||||
block.Header.UTXOCommitment(),
|
||||
block.Header.TimeInMilliseconds(),
|
||||
block.Header.Bits(),
|
||||
block.Header.Nonce(),
|
||||
block.Header.DAAScore(),
|
||||
block.Header.BlueScore(),
|
||||
block.Header.BlueWork(),
|
||||
block.Header.PruningPoint(),
|
||||
)
|
||||
|
||||
_, err = tc.ValidateAndInsertBlock(block, true)
|
||||
if !errors.Is(err, ruleerrors.ErrBlockVersionIsUnknown) {
|
||||
t.Fatalf("Unexpected error: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func CheckBlockTimestampInIsolation(t *testing.T, tc testapi.TestConsensus, cfg *consensus.Config) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ type blockValidator struct {
|
||||
maxBlockParents externalapi.KType
|
||||
timestampDeviationTolerance int
|
||||
targetTimePerBlock time.Duration
|
||||
ignoreHeaderMass bool
|
||||
|
||||
databaseContext model.DBReader
|
||||
difficultyManager model.DifficultyManager
|
||||
@@ -37,6 +38,7 @@ type blockValidator struct {
|
||||
finalityManager model.FinalityManager
|
||||
blockParentBuilder model.BlockParentBuilder
|
||||
pruningManager model.PruningManager
|
||||
parentsManager model.ParentsManager
|
||||
|
||||
blockStore model.BlockStore
|
||||
ghostdagDataStores []model.GHOSTDAGDataStore
|
||||
@@ -57,6 +59,7 @@ func New(powMax *big.Int,
|
||||
maxBlockParents externalapi.KType,
|
||||
timestampDeviationTolerance int,
|
||||
targetTimePerBlock time.Duration,
|
||||
ignoreHeaderMass bool,
|
||||
|
||||
databaseContext model.DBReader,
|
||||
|
||||
@@ -72,6 +75,7 @@ func New(powMax *big.Int,
|
||||
finalityManager model.FinalityManager,
|
||||
blockParentBuilder model.BlockParentBuilder,
|
||||
pruningManager model.PruningManager,
|
||||
parentsManager model.ParentsManager,
|
||||
|
||||
pruningStore model.PruningStore,
|
||||
blockStore model.BlockStore,
|
||||
@@ -92,6 +96,7 @@ func New(powMax *big.Int,
|
||||
maxBlockMass: maxBlockMass,
|
||||
mergeSetSizeLimit: mergeSetSizeLimit,
|
||||
maxBlockParents: maxBlockParents,
|
||||
ignoreHeaderMass: ignoreHeaderMass,
|
||||
|
||||
timestampDeviationTolerance: timestampDeviationTolerance,
|
||||
targetTimePerBlock: targetTimePerBlock,
|
||||
@@ -108,6 +113,7 @@ func New(powMax *big.Int,
|
||||
finalityManager: finalityManager,
|
||||
blockParentBuilder: blockParentBuilder,
|
||||
pruningManager: pruningManager,
|
||||
parentsManager: parentsManager,
|
||||
|
||||
pruningStore: pruningStore,
|
||||
blockStore: blockStore,
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/virtual"
|
||||
"github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -50,9 +49,11 @@ func (v *blockValidator) ValidatePruningPointViolationAndProofOfWorkAndDifficult
|
||||
}
|
||||
}
|
||||
|
||||
err = v.checkProofOfWork(header)
|
||||
if err != nil {
|
||||
return err
|
||||
if !blockHash.Equal(v.genesisHash) {
|
||||
err = v.checkProofOfWork(header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = v.validateDifficulty(stagingArea, blockHash, isBlockWithTrustedData)
|
||||
@@ -68,9 +69,9 @@ func (v *blockValidator) setParents(stagingArea *model.StagingArea,
|
||||
header externalapi.BlockHeader,
|
||||
isBlockWithTrustedData bool) error {
|
||||
|
||||
for level := 0; level <= pow.BlockLevel(header); level++ {
|
||||
for level := 0; level <= header.BlockLevel(); level++ {
|
||||
var parents []*externalapi.DomainHash
|
||||
for _, parent := range header.ParentsAtLevel(level) {
|
||||
for _, parent := range v.parentsManager.ParentsAtLevel(header, level) {
|
||||
_, err := v.ghostdagDataStores[level].Get(v.databaseContext, stagingArea, parent, false)
|
||||
isNotFoundError := database.IsNotFoundError(err)
|
||||
if !isNotFoundError && err != nil {
|
||||
@@ -117,7 +118,7 @@ func (v *blockValidator) validateDifficulty(stagingArea *model.StagingArea,
|
||||
return err
|
||||
}
|
||||
|
||||
blockLevel := pow.BlockLevel(header)
|
||||
blockLevel := header.BlockLevel()
|
||||
for i := 1; i <= blockLevel; i++ {
|
||||
err = v.ghostdagManagers[i].GHOSTDAG(stagingArea, blockHash)
|
||||
if err != nil {
|
||||
@@ -149,7 +150,8 @@ func (v *blockValidator) validateDifficulty(stagingArea *model.StagingArea,
|
||||
// difficulty is not performed.
|
||||
func (v *blockValidator) checkProofOfWork(header externalapi.BlockHeader) error {
|
||||
// The target difficulty must be larger than zero.
|
||||
target := difficulty.CompactToBig(header.Bits())
|
||||
state := pow.NewState(header.ToMutable())
|
||||
target := &state.Target
|
||||
if target.Sign() <= 0 {
|
||||
return errors.Wrapf(ruleerrors.ErrNegativeTarget, "block target difficulty of %064x is too low",
|
||||
target)
|
||||
@@ -163,7 +165,7 @@ func (v *blockValidator) checkProofOfWork(header externalapi.BlockHeader) error
|
||||
|
||||
// The block pow must be valid unless the flag to avoid proof of work checks is set.
|
||||
if !v.skipPoW {
|
||||
valid := pow.CheckProofOfWorkWithTarget(header.ToMutable(), target)
|
||||
valid := state.CheckProofOfWork()
|
||||
if !valid {
|
||||
return errors.Wrap(ruleerrors.ErrInvalidPoW, "block has invalid proof of work")
|
||||
}
|
||||
|
||||
@@ -101,23 +101,26 @@ func TestPOW(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
random := rand.New(rand.NewSource(0))
|
||||
mining.SolveBlock(validBlock, random)
|
||||
_, err = tc.ValidateAndInsertBlock(validBlock, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
// Difficulty is too high on mainnet to actually mine.
|
||||
if consensusConfig.Name != "kaspa-mainnet" {
|
||||
mining.SolveBlock(validBlock, random)
|
||||
_, err = tc.ValidateAndInsertBlock(validBlock, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// solveBlockWithWrongPOW increments the given block's nonce until it gets wrong POW (for test!).
|
||||
func solveBlockWithWrongPOW(block *externalapi.DomainBlock) *externalapi.DomainBlock {
|
||||
targetDifficulty := difficulty.CompactToBig(block.Header.Bits())
|
||||
headerForMining := block.Header.ToMutable()
|
||||
initialNonce := uint64(0)
|
||||
for i := initialNonce; i < math.MaxUint64; i++ {
|
||||
headerForMining.SetNonce(i)
|
||||
if !pow.CheckProofOfWorkWithTarget(headerForMining, targetDifficulty) {
|
||||
block.Header = headerForMining.ToImmutable()
|
||||
header := block.Header.ToMutable()
|
||||
state := pow.NewState(header)
|
||||
for i := uint64(0); i < math.MaxUint64; i++ {
|
||||
state.Nonce = i
|
||||
if !state.CheckProofOfWork() {
|
||||
header.SetNonce(state.Nonce)
|
||||
block.Header = header.ToImmutable()
|
||||
return block
|
||||
}
|
||||
}
|
||||
@@ -296,7 +299,7 @@ func TestCheckPruningPointViolation(t *testing.T) {
|
||||
func TestValidateDifficulty(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
mocDifficulty := &mocDifficultyManager{}
|
||||
mocDifficulty := &mocDifficultyManager{genesisDaaScore: consensusConfig.GenesisBlock.Header.DAAScore()}
|
||||
factory.SetTestDifficultyManager(func(_ model.DBReader, _ model.GHOSTDAGManager, _ model.GHOSTDAGDataStore,
|
||||
_ model.BlockHeaderStore, daaBlocksStore model.DAABlocksStore, _ model.DAGTopologyManager,
|
||||
_ model.DAGTraversalManager, _ *big.Int, _ int, _ bool, _ time.Duration,
|
||||
@@ -342,6 +345,7 @@ type mocDifficultyManager struct {
|
||||
testDifficulty uint32
|
||||
testGenesisBits uint32
|
||||
daaBlocksStore model.DAABlocksStore
|
||||
genesisDaaScore uint64
|
||||
}
|
||||
|
||||
// RequiredDifficulty returns the difficulty required for the test
|
||||
@@ -352,7 +356,7 @@ func (dm *mocDifficultyManager) RequiredDifficulty(*model.StagingArea, *external
|
||||
// StageDAADataAndReturnRequiredDifficulty returns the difficulty required for the test
|
||||
func (dm *mocDifficultyManager) StageDAADataAndReturnRequiredDifficulty(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash, isBlockWithTrustedData bool) (uint32, error) {
|
||||
// Populate daaBlocksStore with fake values
|
||||
dm.daaBlocksStore.StageDAAScore(stagingArea, blockHash, 0)
|
||||
dm.daaBlocksStore.StageDAAScore(stagingArea, blockHash, dm.genesisDaaScore)
|
||||
dm.daaBlocksStore.StageBlockDAAAddedBlocks(stagingArea, blockHash, nil)
|
||||
|
||||
return dm.testDifficulty, nil
|
||||
|
||||
@@ -86,36 +86,5 @@ func TestBlockRewardSwitch(t *testing.T) {
|
||||
t.Fatalf("Subsidy has unexpected value. Want: %d, got: %d", consensusConfig.MinSubsidy, subsidy)
|
||||
}
|
||||
}
|
||||
|
||||
// Add another block. We expect it to be another pruning point
|
||||
lastPruningPointHash, _, err := tc.AddBlock([]*externalapi.DomainHash{tipHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
|
||||
// Make sure that another pruning point had been added
|
||||
pruningPointHeaders, err = tc.PruningPointHeaders()
|
||||
if err != nil {
|
||||
t.Fatalf("PruningPointHeaders: %+v", pruningPointHeaders)
|
||||
}
|
||||
expectedPruningPointHeaderAmount = expectedPruningPointHeaderAmount + 1
|
||||
if uint64(len(pruningPointHeaders)) != expectedPruningPointHeaderAmount {
|
||||
t.Fatalf("Unexpected amount of pruning point headers. "+
|
||||
"Want: %d, got: %d", expectedPruningPointHeaderAmount, len(pruningPointHeaders))
|
||||
}
|
||||
|
||||
// Make sure that the last pruning point has a post-switch subsidy
|
||||
lastPruningPoint, err := tc.GetBlock(lastPruningPointHash)
|
||||
if err != nil {
|
||||
t.Fatalf("GetBlock: %+v", err)
|
||||
}
|
||||
lastPruningPointCoinbase := lastPruningPoint.Transactions[transactionhelper.CoinbaseTransactionIndex]
|
||||
_, _, subsidy, err := tc.CoinbaseManager().ExtractCoinbaseDataBlueScoreAndSubsidy(lastPruningPointCoinbase)
|
||||
if err != nil {
|
||||
t.Fatalf("ExtractCoinbaseDataBlueScoreAndSubsidy: %+v", err)
|
||||
}
|
||||
if subsidy != consensusConfig.SubsidyGenesisReward {
|
||||
t.Fatalf("Subsidy has unexpected value. Want: %d, got: %d", consensusConfig.SubsidyGenesisReward, subsidy)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package coinbasemanager
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math/big"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
@@ -10,7 +12,6 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper"
|
||||
"github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||
"github.com/pkg/errors"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type coinbaseManager struct {
|
||||
@@ -35,7 +36,7 @@ type coinbaseManager struct {
|
||||
}
|
||||
|
||||
func (c *coinbaseManager) ExpectedCoinbaseTransaction(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash,
|
||||
coinbaseData *externalapi.DomainCoinbaseData, blockPruningPoint *externalapi.DomainHash) (*externalapi.DomainTransaction, error) {
|
||||
coinbaseData *externalapi.DomainCoinbaseData) (*externalapi.DomainTransaction, error) {
|
||||
|
||||
ghostdagData, err := c.ghostdagDataStore.Get(c.databaseContext, stagingArea, blockHash, true)
|
||||
if !database.IsNotFoundError(err) && err != nil {
|
||||
@@ -83,7 +84,7 @@ func (c *coinbaseManager) ExpectedCoinbaseTransaction(stagingArea *model.Staging
|
||||
txOuts = append(txOuts, txOut)
|
||||
}
|
||||
|
||||
subsidy, err := c.CalcBlockSubsidy(stagingArea, blockHash, blockPruningPoint)
|
||||
subsidy, err := c.CalcBlockSubsidy(blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -183,58 +184,12 @@ func acceptanceDataFromArrayToMap(acceptanceData externalapi.AcceptanceData) map
|
||||
// has the expected value.
|
||||
//
|
||||
// Further details: https://hashdag.medium.com/kaspa-launch-plan-9a63f4d754a6
|
||||
func (c *coinbaseManager) CalcBlockSubsidy(stagingArea *model.StagingArea,
|
||||
blockHash *externalapi.DomainHash, blockPruningPoint *externalapi.DomainHash) (uint64, error) {
|
||||
|
||||
func (c *coinbaseManager) CalcBlockSubsidy(blockHash *externalapi.DomainHash) (uint64, error) {
|
||||
if blockHash.Equal(c.genesisHash) {
|
||||
return c.subsidyGenesisReward, nil
|
||||
}
|
||||
|
||||
isBlockRewardFixed, err := c.isBlockRewardFixed(stagingArea, blockPruningPoint)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if isBlockRewardFixed {
|
||||
return c.subsidyGenesisReward, nil
|
||||
}
|
||||
|
||||
averagePastSubsidy, err := c.calculateAveragePastSubsidy(stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
mergeSetSubsidySum, err := c.calculateMergeSetSubsidySum(stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
subsidyRandomVariable, err := c.calculateSubsidyRandomVariable(stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
pastSubsidy := new(big.Rat).Mul(averagePastSubsidy, c.subsidyPastRewardMultiplier)
|
||||
mergeSetSubsidy := new(big.Rat).Mul(mergeSetSubsidySum, c.subsidyMergeSetRewardMultiplier)
|
||||
|
||||
// In order to avoid unsupported negative exponents in powInt64, flip
|
||||
// the numerator and the denominator manually
|
||||
subsidyRandom := new(big.Rat)
|
||||
if subsidyRandomVariable >= 0 {
|
||||
subsidyRandom = subsidyRandom.SetInt64(1 << subsidyRandomVariable)
|
||||
} else {
|
||||
subsidyRandom = subsidyRandom.SetFrac64(1, 1<<(-subsidyRandomVariable))
|
||||
}
|
||||
|
||||
blockSubsidyBigRat := new(big.Rat).Add(mergeSetSubsidy, new(big.Rat).Mul(pastSubsidy, subsidyRandom))
|
||||
blockSubsidyBigInt := new(big.Int).Div(blockSubsidyBigRat.Num(), blockSubsidyBigRat.Denom())
|
||||
blockSubsidyUint64 := blockSubsidyBigInt.Uint64()
|
||||
|
||||
clampedBlockSubsidy := blockSubsidyUint64
|
||||
if clampedBlockSubsidy < c.minSubsidy {
|
||||
clampedBlockSubsidy = c.minSubsidy
|
||||
} else if clampedBlockSubsidy > c.maxSubsidy {
|
||||
clampedBlockSubsidy = c.maxSubsidy
|
||||
}
|
||||
|
||||
return clampedBlockSubsidy, nil
|
||||
return c.maxSubsidy, nil
|
||||
}
|
||||
|
||||
func (c *coinbaseManager) calculateAveragePastSubsidy(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (*big.Rat, error) {
|
||||
|
||||
@@ -22,12 +22,12 @@ func TestVirtualDiff(t *testing.T) {
|
||||
defer teardown(false)
|
||||
|
||||
// Add block A over the genesis
|
||||
blockAHash, blockInsertionResult, err := tc.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
blockAHash, virtualChangeSet, err := tc.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error adding block A: %+v", err)
|
||||
}
|
||||
|
||||
virtualUTXODiff := blockInsertionResult.VirtualUTXODiff
|
||||
virtualUTXODiff := virtualChangeSet.VirtualUTXODiff
|
||||
if virtualUTXODiff.ToRemove().Len() != 0 {
|
||||
t.Fatalf("Unexpected length %d for virtualUTXODiff.ToRemove()", virtualUTXODiff.ToRemove().Len())
|
||||
}
|
||||
@@ -37,7 +37,7 @@ func TestVirtualDiff(t *testing.T) {
|
||||
t.Fatalf("Unexpected length %d for virtualUTXODiff.ToAdd()", virtualUTXODiff.ToAdd().Len())
|
||||
}
|
||||
|
||||
blockBHash, blockInsertionResult, err := tc.AddBlock([]*externalapi.DomainHash{blockAHash}, nil, nil)
|
||||
blockBHash, virtualChangeSet, err := tc.AddBlock([]*externalapi.DomainHash{blockAHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error adding block A: %+v", err)
|
||||
}
|
||||
@@ -47,7 +47,7 @@ func TestVirtualDiff(t *testing.T) {
|
||||
t.Fatalf("Block: %+v", err)
|
||||
}
|
||||
|
||||
virtualUTXODiff = blockInsertionResult.VirtualUTXODiff
|
||||
virtualUTXODiff = virtualChangeSet.VirtualUTXODiff
|
||||
if virtualUTXODiff.ToRemove().Len() != 0 {
|
||||
t.Fatalf("Unexpected length %d for virtualUTXODiff.ToRemove()", virtualUTXODiff.ToRemove().Len())
|
||||
}
|
||||
@@ -75,7 +75,7 @@ func TestVirtualDiff(t *testing.T) {
|
||||
blockB.Transactions[0].Outputs[0].Value,
|
||||
blockB.Transactions[0].Outputs[0].ScriptPublicKey,
|
||||
true,
|
||||
2, //Expected virtual DAA score
|
||||
consensusConfig.GenesisBlock.Header.DAAScore()+2, //Expected virtual DAA score
|
||||
)) {
|
||||
t.Fatalf("Unexpected entry %s", entry)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package consensusstatemanager
|
||||
|
||||
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"
|
||||
@@ -23,8 +22,16 @@ func (csm *consensusStateManager) CalculatePastUTXOAndAcceptanceData(stagingArea
|
||||
|
||||
if blockHash.Equal(csm.genesisHash) {
|
||||
log.Debugf("Block %s is the genesis. By definition, "+
|
||||
"it has an empty UTXO diff, empty acceptance data, and a blank multiset", blockHash)
|
||||
return utxo.NewUTXODiff(), externalapi.AcceptanceData{}, multiset.New(), nil
|
||||
"it has a predefined UTXO diff, empty acceptance data, and a predefined multiset", blockHash)
|
||||
multiset, err := csm.multisetStore.Get(csm.databaseContext, stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
utxoDiff, err := csm.utxoDiffStore.UTXODiff(csm.databaseContext, stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
return utxoDiff, externalapi.AcceptanceData{}, multiset, nil
|
||||
}
|
||||
|
||||
blockGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, stagingArea, blockHash, false)
|
||||
|
||||
@@ -19,11 +19,11 @@ func TestCalculateChainPath(t *testing.T) {
|
||||
defer teardown(false)
|
||||
|
||||
// Add block A over the genesis
|
||||
blockAHash, blockAInsertionResult, err := consensus.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
blockAHash, blockAVirtualChangeSet, err := consensus.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error adding block A: %+v", err)
|
||||
}
|
||||
blockASelectedParentChainChanges := blockAInsertionResult.VirtualSelectedParentChainChanges
|
||||
blockASelectedParentChainChanges := blockAVirtualChangeSet.VirtualSelectedParentChainChanges
|
||||
|
||||
// Make sure that the removed slice is empty
|
||||
if len(blockASelectedParentChainChanges.Removed) > 0 {
|
||||
@@ -59,11 +59,11 @@ func TestCalculateChainPath(t *testing.T) {
|
||||
|
||||
// Add block C over the block that isn't the current virtual's selected parent
|
||||
// We expect this to cause a reorg
|
||||
blockCHash, blockCInsertionResult, err := consensus.AddBlock([]*externalapi.DomainHash{notVirtualSelectedParent}, nil, nil)
|
||||
blockCHash, blockCVirtualChangeSet, err := consensus.AddBlock([]*externalapi.DomainHash{notVirtualSelectedParent}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error adding block C: %+v", err)
|
||||
}
|
||||
blockCSelectedParentChainChanges := blockCInsertionResult.VirtualSelectedParentChainChanges
|
||||
blockCSelectedParentChainChanges := blockCVirtualChangeSet.VirtualSelectedParentChainChanges
|
||||
|
||||
// Make sure that the removed slice contains only the block that was previously
|
||||
// the selected parent
|
||||
@@ -92,11 +92,11 @@ func TestCalculateChainPath(t *testing.T) {
|
||||
}
|
||||
|
||||
// Add block D over the genesis
|
||||
_, blockDInsertionResult, err := consensus.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
_, blockDVirtualChangeSet, err := consensus.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error adding block D: %+v", err)
|
||||
}
|
||||
blockDSelectedParentChainChanges := blockDInsertionResult.VirtualSelectedParentChainChanges
|
||||
blockDSelectedParentChainChanges := blockDVirtualChangeSet.VirtualSelectedParentChainChanges
|
||||
|
||||
// Make sure that both the added and the removed slices are empty
|
||||
if len(blockDSelectedParentChainChanges.Added) > 0 {
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/multiset"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||
)
|
||||
|
||||
@@ -19,8 +18,8 @@ func (csm *consensusStateManager) calculateMultiset(stagingArea *model.StagingAr
|
||||
|
||||
if blockHash.Equal(csm.genesisHash) {
|
||||
log.Debugf("Selected parent is nil, which could only happen for the genesis. " +
|
||||
"The genesis, by definition, has an empty multiset")
|
||||
return multiset.New(), nil
|
||||
"The genesis has a predefined multiset")
|
||||
return csm.multisetStore.Get(csm.databaseContext, stagingArea, blockHash)
|
||||
}
|
||||
|
||||
ms, err := csm.multisetStore.Get(csm.databaseContext, stagingArea, blockGHOSTDAGData.SelectedParent())
|
||||
|
||||
@@ -59,7 +59,8 @@ func (csm *consensusStateManager) pickVirtualParents(stagingArea *model.StagingA
|
||||
selectedVirtualParents := []*externalapi.DomainHash{virtualSelectedParent}
|
||||
mergeSetSize := uint64(1) // starts counting from 1 because selectedParent is already in the mergeSet
|
||||
|
||||
for len(candidates) > 0 && uint64(len(selectedVirtualParents)) < uint64(csm.maxBlockParents) {
|
||||
// First condition implies that no point in searching since limit was already reached
|
||||
for mergeSetSize < csm.mergeSetSizeLimit && len(candidates) > 0 && uint64(len(selectedVirtualParents)) < uint64(csm.maxBlockParents) {
|
||||
candidate := candidates[0]
|
||||
candidates = candidates[1:]
|
||||
|
||||
|
||||
@@ -8,14 +8,14 @@ import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
func (csm *consensusStateManager) ResolveVirtual(maxBlocksToResolve uint64) (bool, error) {
|
||||
func (csm *consensusStateManager) ResolveVirtual(maxBlocksToResolve uint64) (*externalapi.VirtualChangeSet, bool, error) {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "csm.ResolveVirtual")
|
||||
defer onEnd()
|
||||
|
||||
readStagingArea := model.NewStagingArea()
|
||||
tips, err := csm.consensusStateStore.Tips(readStagingArea, csm.databaseContext)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
var sortErr error
|
||||
@@ -29,29 +29,29 @@ func (csm *consensusStateManager) ResolveVirtual(maxBlocksToResolve uint64) (boo
|
||||
return selectedParent.Equal(tips[i])
|
||||
})
|
||||
if sortErr != nil {
|
||||
return false, sortErr
|
||||
return nil, false, sortErr
|
||||
}
|
||||
|
||||
var selectedTip *externalapi.DomainHash
|
||||
isCompletelyResolved := true
|
||||
for _, tip := range tips {
|
||||
log.Infof("Resolving tip %s", tip)
|
||||
log.Debugf("Resolving tip %s", tip)
|
||||
resolveStagingArea := model.NewStagingArea()
|
||||
unverifiedBlocks, err := csm.getUnverifiedChainBlocks(resolveStagingArea, tip)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
resolveTip := tip
|
||||
hasMoreUnverifiedThanMax := maxBlocksToResolve != 0 && uint64(len(unverifiedBlocks)) > maxBlocksToResolve
|
||||
if hasMoreUnverifiedThanMax {
|
||||
resolveTip = unverifiedBlocks[uint64(len(unverifiedBlocks))-maxBlocksToResolve]
|
||||
log.Infof("Has more than %d blocks to resolve. Changing the resolve tip to %s", maxBlocksToResolve, resolveTip)
|
||||
log.Debugf("Has more than %d blocks to resolve. Changing the resolve tip to %s", maxBlocksToResolve, resolveTip)
|
||||
}
|
||||
|
||||
blockStatus, reversalData, err := csm.resolveBlockStatus(resolveStagingArea, resolveTip, true)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if blockStatus == externalapi.StatusUTXOValid {
|
||||
@@ -60,13 +60,13 @@ func (csm *consensusStateManager) ResolveVirtual(maxBlocksToResolve uint64) (boo
|
||||
|
||||
err = staging.CommitAllChanges(csm.databaseContext, resolveStagingArea)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if reversalData != nil {
|
||||
err = csm.ReverseUTXODiffs(resolveTip, reversalData)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return nil, false, err
|
||||
}
|
||||
}
|
||||
break
|
||||
@@ -75,19 +75,39 @@ func (csm *consensusStateManager) ResolveVirtual(maxBlocksToResolve uint64) (boo
|
||||
|
||||
if selectedTip == nil {
|
||||
log.Warnf("Non of the DAG tips are valid")
|
||||
return true, nil
|
||||
return nil, true, nil
|
||||
}
|
||||
|
||||
oldVirtualGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, readStagingArea, model.VirtualBlockHash, false)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
updateVirtualStagingArea := model.NewStagingArea()
|
||||
_, err = csm.updateVirtualWithParents(updateVirtualStagingArea, []*externalapi.DomainHash{selectedTip})
|
||||
virtualUTXODiff, err := csm.updateVirtualWithParents(updateVirtualStagingArea, []*externalapi.DomainHash{selectedTip})
|
||||
if err != nil {
|
||||
return false, err
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
err = staging.CommitAllChanges(csm.databaseContext, updateVirtualStagingArea)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
return isCompletelyResolved, nil
|
||||
selectedParentChainChanges, err := csm.dagTraversalManager.
|
||||
CalculateChainPath(readStagingArea, oldVirtualGHOSTDAGData.SelectedParent(), selectedTip)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
virtualParents, err := csm.dagTopologyManager.Parents(readStagingArea, model.VirtualBlockHash)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
return &externalapi.VirtualChangeSet{
|
||||
VirtualSelectedParentChainChanges: selectedParentChainChanges,
|
||||
VirtualUTXODiff: virtualUTXODiff,
|
||||
VirtualParents: virtualParents,
|
||||
}, isCompletelyResolved, nil
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@ package consensusstatemanager
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||
|
||||
"github.com/kaspanet/kaspad/util/staging"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
@@ -129,7 +127,11 @@ func (csm *consensusStateManager) selectedParentInfo(
|
||||
if lastUnverifiedBlock.Equal(csm.genesisHash) {
|
||||
log.Debugf("the most recent unverified block is the genesis block, "+
|
||||
"which by definition has status: %s", externalapi.StatusUTXOValid)
|
||||
return lastUnverifiedBlock, externalapi.StatusUTXOValid, utxo.NewUTXODiff(), nil
|
||||
utxoDiff, err := csm.utxoDiffStore.UTXODiff(csm.databaseContext, stagingArea, lastUnverifiedBlock)
|
||||
if err != nil {
|
||||
return nil, 0, nil, err
|
||||
}
|
||||
return lastUnverifiedBlock, externalapi.StatusUTXOValid, utxoDiff, nil
|
||||
}
|
||||
lastUnverifiedBlockGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, stagingArea, lastUnverifiedBlock, false)
|
||||
if err != nil {
|
||||
|
||||
@@ -285,7 +285,7 @@ func TestTransactionAcceptance(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting blockF: %+v", err)
|
||||
}
|
||||
updatedDAAScoreVirtualBlock := 26
|
||||
updatedDAAScoreVirtualBlock := consensusConfig.GenesisBlock.Header.DAAScore() + 26
|
||||
//We expect the second transaction in the "blue block" (blueChildOfRedBlock) to be accepted because the merge set is ordered topologically
|
||||
//and the red block is ordered topologically before the "blue block" so the input is known in the UTXOSet.
|
||||
expectedAcceptanceData := externalapi.AcceptanceData{
|
||||
|
||||
@@ -117,6 +117,10 @@ func (csm *consensusStateManager) validateUTXOCommitment(
|
||||
log.Tracef("validateUTXOCommitment start for block %s", blockHash)
|
||||
defer log.Tracef("validateUTXOCommitment end for block %s", blockHash)
|
||||
|
||||
if blockHash.Equal(csm.genesisHash) {
|
||||
return nil
|
||||
}
|
||||
|
||||
multisetHash := multiset.Hash()
|
||||
if !block.Header.UTXOCommitment().Equal(multisetHash) {
|
||||
return errors.Wrapf(ruleerrors.ErrBadUTXOCommitment, "block %s UTXO commitment is invalid - block "+
|
||||
@@ -162,12 +166,8 @@ func (csm *consensusStateManager) validateCoinbaseTransaction(stagingArea *model
|
||||
}
|
||||
|
||||
log.Tracef("Calculating the expected coinbase transaction for the given coinbase data and block %s", blockHash)
|
||||
header, err := csm.blockHeaderStore.BlockHeader(csm.databaseContext, stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
expectedCoinbaseTransaction, err :=
|
||||
csm.coinbaseManager.ExpectedCoinbaseTransaction(stagingArea, blockHash, coinbaseData, header.PruningPoint())
|
||||
csm.coinbaseManager.ExpectedCoinbaseTransaction(stagingArea, blockHash, coinbaseData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
type selectedChildIterator struct {
|
||||
dagTraversalManager model.DAGTraversalManager
|
||||
|
||||
includeLowHash bool
|
||||
highHash, lowHash *externalapi.DomainHash
|
||||
current *externalapi.DomainHash
|
||||
err error
|
||||
@@ -21,6 +22,10 @@ func (s *selectedChildIterator) First() bool {
|
||||
panic("Tried using a closed SelectedChildIterator")
|
||||
}
|
||||
s.current = s.lowHash
|
||||
if s.includeLowHash {
|
||||
return true
|
||||
}
|
||||
|
||||
return s.Next()
|
||||
}
|
||||
|
||||
@@ -67,7 +72,9 @@ func (s *selectedChildIterator) Close() error {
|
||||
|
||||
// SelectedChildIterator returns a BlockIterator that iterates from lowHash (exclusive) to highHash (inclusive) over
|
||||
// highHash's selected parent chain
|
||||
func (dtm *dagTraversalManager) SelectedChildIterator(stagingArea *model.StagingArea, highHash, lowHash *externalapi.DomainHash) (model.BlockIterator, error) {
|
||||
func (dtm *dagTraversalManager) SelectedChildIterator(stagingArea *model.StagingArea,
|
||||
highHash, lowHash *externalapi.DomainHash, includeLowHash bool) (model.BlockIterator, error) {
|
||||
|
||||
isLowHashInSelectedParentChainOfHighHash, err := dtm.dagTopologyManager.IsInSelectedParentChainOf(
|
||||
stagingArea, lowHash, highHash)
|
||||
if err != nil {
|
||||
@@ -79,6 +86,7 @@ func (dtm *dagTraversalManager) SelectedChildIterator(stagingArea *model.Staging
|
||||
}
|
||||
return &selectedChildIterator{
|
||||
dagTraversalManager: dtm,
|
||||
includeLowHash: includeLowHash,
|
||||
highHash: highHash,
|
||||
lowHash: lowHash,
|
||||
current: lowHash,
|
||||
|
||||
@@ -60,37 +60,38 @@ func TestBlockWindow(t *testing.T) {
|
||||
{
|
||||
parents: []string{"H", "F"},
|
||||
id: "I",
|
||||
expectedWindow: []string{"F", "H", "C", "D", "B", "G"},
|
||||
expectedWindow: []string{"F", "C", "H", "D", "B", "G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"I"},
|
||||
id: "J",
|
||||
expectedWindow: []string{"I", "F", "H", "C", "D", "B", "G"},
|
||||
expectedWindow: []string{"I", "F", "C", "H", "D", "B", "G"},
|
||||
},
|
||||
//
|
||||
{
|
||||
parents: []string{"J"},
|
||||
id: "K",
|
||||
expectedWindow: []string{"J", "I", "F", "H", "C", "D", "B", "G"},
|
||||
expectedWindow: []string{"J", "I", "F", "C", "H", "D", "B", "G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"K"},
|
||||
id: "L",
|
||||
expectedWindow: []string{"K", "J", "I", "F", "H", "C", "D", "B", "G"},
|
||||
expectedWindow: []string{"K", "J", "I", "F", "C", "H", "D", "B", "G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"L"},
|
||||
id: "M",
|
||||
expectedWindow: []string{"L", "K", "J", "I", "F", "H", "C", "D", "B", "G"},
|
||||
expectedWindow: []string{"L", "K", "J", "I", "F", "C", "H", "D", "B", "G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"M"},
|
||||
id: "N",
|
||||
expectedWindow: []string{"M", "L", "K", "J", "I", "F", "H", "C", "D", "B"},
|
||||
expectedWindow: []string{"M", "L", "K", "J", "I", "F", "C", "H", "D", "B"},
|
||||
},
|
||||
{
|
||||
parents: []string{"N"},
|
||||
id: "O",
|
||||
expectedWindow: []string{"N", "M", "L", "K", "J", "I", "F", "H", "C", "D"},
|
||||
expectedWindow: []string{"N", "M", "L", "K", "J", "I", "F", "C", "H", "D"},
|
||||
},
|
||||
},
|
||||
dagconfig.TestnetParams.Name: {
|
||||
@@ -132,37 +133,37 @@ func TestBlockWindow(t *testing.T) {
|
||||
{
|
||||
parents: []string{"H", "F"},
|
||||
id: "I",
|
||||
expectedWindow: []string{"F", "H", "C", "D", "G", "B"},
|
||||
expectedWindow: []string{"F", "C", "D", "H", "B", "G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"I"},
|
||||
id: "J",
|
||||
expectedWindow: []string{"I", "F", "H", "C", "D", "G", "B"},
|
||||
expectedWindow: []string{"I", "F", "C", "D", "H", "B", "G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"J"},
|
||||
id: "K",
|
||||
expectedWindow: []string{"J", "I", "F", "H", "C", "D", "G", "B"},
|
||||
expectedWindow: []string{"J", "I", "F", "C", "D", "H", "B", "G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"K"},
|
||||
id: "L",
|
||||
expectedWindow: []string{"K", "J", "I", "F", "H", "C", "D", "G", "B"},
|
||||
expectedWindow: []string{"K", "J", "I", "F", "C", "D", "H", "B", "G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"L"},
|
||||
id: "M",
|
||||
expectedWindow: []string{"L", "K", "J", "I", "F", "H", "C", "D", "G", "B"},
|
||||
expectedWindow: []string{"L", "K", "J", "I", "F", "C", "D", "H", "B", "G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"M"},
|
||||
id: "N",
|
||||
expectedWindow: []string{"M", "L", "K", "J", "I", "F", "H", "C", "D", "G"},
|
||||
expectedWindow: []string{"M", "L", "K", "J", "I", "F", "C", "D", "H", "B"},
|
||||
},
|
||||
{
|
||||
parents: []string{"N"},
|
||||
id: "O",
|
||||
expectedWindow: []string{"N", "M", "L", "K", "J", "I", "F", "H", "C", "D"},
|
||||
expectedWindow: []string{"N", "M", "L", "K", "J", "I", "F", "C", "D", "H"},
|
||||
},
|
||||
},
|
||||
dagconfig.DevnetParams.Name: {
|
||||
@@ -201,78 +202,6 @@ func TestBlockWindow(t *testing.T) {
|
||||
id: "H",
|
||||
expectedWindow: []string{"G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"H", "F"},
|
||||
id: "I",
|
||||
expectedWindow: []string{"F", "D", "C", "H", "B", "G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"I"},
|
||||
id: "J",
|
||||
expectedWindow: []string{"I", "F", "D", "C", "H", "B", "G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"J"},
|
||||
id: "K",
|
||||
expectedWindow: []string{"J", "I", "F", "D", "C", "H", "B", "G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"K"},
|
||||
id: "L",
|
||||
expectedWindow: []string{"K", "J", "I", "F", "D", "C", "H", "B", "G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"L"},
|
||||
id: "M",
|
||||
expectedWindow: []string{"L", "K", "J", "I", "F", "D", "C", "H", "B", "G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"M"},
|
||||
id: "N",
|
||||
expectedWindow: []string{"M", "L", "K", "J", "I", "F", "D", "C", "H", "B"},
|
||||
},
|
||||
{
|
||||
parents: []string{"N"},
|
||||
id: "O",
|
||||
expectedWindow: []string{"N", "M", "L", "K", "J", "I", "F", "D", "C", "H"},
|
||||
},
|
||||
},
|
||||
dagconfig.SimnetParams.Name: {
|
||||
{
|
||||
parents: []string{"A"},
|
||||
id: "B",
|
||||
expectedWindow: []string{},
|
||||
},
|
||||
{
|
||||
parents: []string{"B"},
|
||||
id: "C",
|
||||
expectedWindow: []string{"B"},
|
||||
},
|
||||
{
|
||||
parents: []string{"B"},
|
||||
id: "D",
|
||||
expectedWindow: []string{"B"},
|
||||
},
|
||||
{
|
||||
parents: []string{"D", "C"},
|
||||
id: "E",
|
||||
expectedWindow: []string{"D", "C", "B"},
|
||||
},
|
||||
{
|
||||
parents: []string{"D", "C"},
|
||||
id: "F",
|
||||
expectedWindow: []string{"D", "C", "B"},
|
||||
},
|
||||
{
|
||||
parents: []string{"A"},
|
||||
id: "G",
|
||||
expectedWindow: []string{},
|
||||
},
|
||||
{
|
||||
parents: []string{"G"},
|
||||
id: "H",
|
||||
expectedWindow: []string{"G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"H", "F"},
|
||||
id: "I",
|
||||
@@ -309,6 +238,78 @@ func TestBlockWindow(t *testing.T) {
|
||||
expectedWindow: []string{"N", "M", "L", "K", "J", "I", "F", "D", "H", "C"},
|
||||
},
|
||||
},
|
||||
dagconfig.SimnetParams.Name: {
|
||||
{
|
||||
parents: []string{"A"},
|
||||
id: "B",
|
||||
expectedWindow: []string{},
|
||||
},
|
||||
{
|
||||
parents: []string{"B"},
|
||||
id: "C",
|
||||
expectedWindow: []string{"B"},
|
||||
},
|
||||
{
|
||||
parents: []string{"B"},
|
||||
id: "D",
|
||||
expectedWindow: []string{"B"},
|
||||
},
|
||||
{
|
||||
parents: []string{"D", "C"},
|
||||
id: "E",
|
||||
expectedWindow: []string{"D", "C", "B"},
|
||||
},
|
||||
{
|
||||
parents: []string{"D", "C"},
|
||||
id: "F",
|
||||
expectedWindow: []string{"D", "C", "B"},
|
||||
},
|
||||
{
|
||||
parents: []string{"A"},
|
||||
id: "G",
|
||||
expectedWindow: []string{},
|
||||
},
|
||||
{
|
||||
parents: []string{"G"},
|
||||
id: "H",
|
||||
expectedWindow: []string{"G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"H", "F"},
|
||||
id: "I",
|
||||
expectedWindow: []string{"F", "H", "D", "C", "B", "G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"I"},
|
||||
id: "J",
|
||||
expectedWindow: []string{"I", "F", "H", "D", "C", "B", "G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"J"},
|
||||
id: "K",
|
||||
expectedWindow: []string{"J", "I", "F", "H", "D", "C", "B", "G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"K"},
|
||||
id: "L",
|
||||
expectedWindow: []string{"K", "J", "I", "F", "H", "D", "C", "B", "G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"L"},
|
||||
id: "M",
|
||||
expectedWindow: []string{"L", "K", "J", "I", "F", "H", "D", "C", "B", "G"},
|
||||
},
|
||||
{
|
||||
parents: []string{"M"},
|
||||
id: "N",
|
||||
expectedWindow: []string{"M", "L", "K", "J", "I", "F", "H", "D", "C", "B"},
|
||||
},
|
||||
{
|
||||
parents: []string{"N"},
|
||||
id: "O",
|
||||
expectedWindow: []string{"N", "M", "L", "K", "J", "I", "F", "H", "D", "C"},
|
||||
},
|
||||
},
|
||||
}
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
consensusConfig.K = 1
|
||||
|
||||
@@ -105,10 +105,14 @@ func (dm *difficultyManager) requiredDifficultyFromTargetsWindow(targetsWindow b
|
||||
return dm.genesisBits, nil
|
||||
}
|
||||
|
||||
// in the past this was < 2 as the comment explains, we changed it to under the window size to
|
||||
// make the hashrate(which is ~1.5GH/s) constant in the first 2641 blocks so that we won't have a lot of tips
|
||||
|
||||
// We need at least 2 blocks to get a timestamp interval
|
||||
// We could instead clamp the timestamp difference to `targetTimePerBlock`,
|
||||
// but then everything will cancel out and we'll get the target from the last block, which will be the same as genesis.
|
||||
if len(targetsWindow) < 2 {
|
||||
// We add 64 as a safety margin
|
||||
if len(targetsWindow) < 2 || len(targetsWindow) < dm.difficultyAdjustmentWindowSize {
|
||||
return dm.genesisBits, nil
|
||||
}
|
||||
windowMinTimestamp, windowMaxTimeStamp, windowsMinIndex, _ := targetsWindow.minMaxTimestamps()
|
||||
@@ -157,7 +161,11 @@ func (dm *difficultyManager) calculateDaaScoreAndAddedBlocks(stagingArea *model.
|
||||
isBlockWithTrustedData bool) (uint64, []*externalapi.DomainHash, error) {
|
||||
|
||||
if blockHash.Equal(dm.genesisHash) {
|
||||
return 0, nil, nil
|
||||
genesisHeader, err := dm.headerStore.BlockHeader(dm.databaseContext, stagingArea, dm.genesisHash)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return genesisHeader.DAAScore(), nil, nil
|
||||
}
|
||||
|
||||
ghostdagData, err := dm.ghostdagStore.Get(dm.databaseContext, stagingArea, blockHash, false)
|
||||
|
||||
@@ -19,12 +19,6 @@ import (
|
||||
|
||||
func TestDifficulty(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
// Mainnet's genesis is too new, so if we'll build on it we'll get to the future very quickly.
|
||||
// TODO: Once it gets older, we should unskip this test.
|
||||
if consensusConfig.Name == "kaspa-mainnet" {
|
||||
return
|
||||
}
|
||||
|
||||
if consensusConfig.DisableDifficultyAdjustment {
|
||||
return
|
||||
}
|
||||
@@ -37,7 +31,7 @@ func TestDifficulty(t *testing.T) {
|
||||
}
|
||||
|
||||
consensusConfig.K = 1
|
||||
consensusConfig.DifficultyAdjustmentWindowSize = 265
|
||||
consensusConfig.DifficultyAdjustmentWindowSize = 140
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestDifficulty")
|
||||
@@ -114,7 +108,7 @@ func TestDifficulty(t *testing.T) {
|
||||
"window size, the difficulty should be the same as genesis'")
|
||||
}
|
||||
}
|
||||
for i := 0; i < consensusConfig.DifficultyAdjustmentWindowSize+100; i++ {
|
||||
for i := 0; i < consensusConfig.DifficultyAdjustmentWindowSize+10; i++ {
|
||||
tip, tipHash = addBlock(0, tipHash)
|
||||
if tip.Header.Bits() != consensusConfig.GenesisBlock.Header.Bits() {
|
||||
t.Fatalf("As long as the block rate remains the same, the difficulty shouldn't change")
|
||||
@@ -135,10 +129,12 @@ func TestDifficulty(t *testing.T) {
|
||||
|
||||
var expectedBits uint32
|
||||
switch consensusConfig.Name {
|
||||
case dagconfig.TestnetParams.Name, dagconfig.DevnetParams.Name:
|
||||
expectedBits = uint32(0x1e7f83df)
|
||||
case dagconfig.TestnetParams.Name:
|
||||
expectedBits = uint32(0x1e7f1441)
|
||||
case dagconfig.DevnetParams.Name:
|
||||
expectedBits = uint32(0x207f1441)
|
||||
case dagconfig.MainnetParams.Name:
|
||||
expectedBits = uint32(0x1e7f83df)
|
||||
expectedBits = uint32(0x1d02c50f)
|
||||
}
|
||||
|
||||
if tip.Header.Bits() != expectedBits {
|
||||
@@ -237,7 +233,7 @@ func TestDifficulty(t *testing.T) {
|
||||
|
||||
func TestDAAScore(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
consensusConfig.DifficultyAdjustmentWindowSize = 265
|
||||
consensusConfig.DifficultyAdjustmentWindowSize = 86
|
||||
|
||||
stagingArea := model.NewStagingArea()
|
||||
|
||||
@@ -269,9 +265,9 @@ func TestDAAScore(t *testing.T) {
|
||||
t.Fatalf("DAAScore: %+v", err)
|
||||
}
|
||||
|
||||
blockBlueScore3ExpectedDAAScore := uint64(2)
|
||||
blockBlueScore3ExpectedDAAScore := uint64(2) + consensusConfig.GenesisBlock.Header.DAAScore()
|
||||
if blockBlueScore3DAAScore != blockBlueScore3ExpectedDAAScore {
|
||||
t.Fatalf("DAA score is expected to be %d but got %d", blockBlueScore3ExpectedDAAScore, blockBlueScore3ExpectedDAAScore)
|
||||
t.Fatalf("DAA score is expected to be %d but got %d", blockBlueScore3ExpectedDAAScore, blockBlueScore3DAAScore)
|
||||
}
|
||||
tipDAAScore := blockBlueScore3ExpectedDAAScore
|
||||
|
||||
|
||||
41
domain/consensus/processes/parentsmanager/parentsmanager.go
Normal file
41
domain/consensus/processes/parentsmanager/parentsmanager.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package parentssanager
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
)
|
||||
|
||||
type parentsManager struct {
|
||||
genesisHash *externalapi.DomainHash
|
||||
}
|
||||
|
||||
// New instantiates a new ParentsManager
|
||||
func New(genesisHash *externalapi.DomainHash) model.ParentsManager {
|
||||
return &parentsManager{
|
||||
genesisHash: genesisHash,
|
||||
}
|
||||
}
|
||||
|
||||
func (pm *parentsManager) ParentsAtLevel(blockHeader externalapi.BlockHeader, level int) externalapi.BlockLevelParents {
|
||||
var parentsAtLevel externalapi.BlockLevelParents
|
||||
if len(blockHeader.Parents()) > level {
|
||||
parentsAtLevel = blockHeader.Parents()[level]
|
||||
}
|
||||
|
||||
if len(parentsAtLevel) == 0 && len(blockHeader.DirectParents()) > 0 {
|
||||
return externalapi.BlockLevelParents{pm.genesisHash}
|
||||
}
|
||||
|
||||
return parentsAtLevel
|
||||
}
|
||||
|
||||
func (pm *parentsManager) Parents(blockHeader externalapi.BlockHeader) []externalapi.BlockLevelParents {
|
||||
numParents := constants.MaxBlockLevel + 1
|
||||
parents := make([]externalapi.BlockLevelParents, numParents)
|
||||
for i := 0; i < numParents; i++ {
|
||||
parents[i] = pm.ParentsAtLevel(blockHeader, i)
|
||||
}
|
||||
|
||||
return parents
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package pruningmanager_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -37,14 +36,16 @@ func TestPruning(t *testing.T) {
|
||||
dagconfig.SimnetParams.Name: "1582",
|
||||
},
|
||||
"dag-for-test-pruning.json": {
|
||||
dagconfig.MainnetParams.Name: "502",
|
||||
dagconfig.MainnetParams.Name: "503",
|
||||
dagconfig.TestnetParams.Name: "502",
|
||||
dagconfig.DevnetParams.Name: "502",
|
||||
dagconfig.DevnetParams.Name: "503",
|
||||
dagconfig.SimnetParams.Name: "502",
|
||||
},
|
||||
}
|
||||
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
// Improve the performance of the test a little
|
||||
consensusConfig.DisableDifficultyAdjustment = true
|
||||
err := filepath.Walk("./testdata", func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -72,6 +73,7 @@ func TestPruning(t *testing.T) {
|
||||
consensusConfig.DifficultyAdjustmentWindowSize = 400
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
factory.SetTestLevelDBCacheSize(128)
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestPruning")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
@@ -140,12 +142,11 @@ func TestPruning(t *testing.T) {
|
||||
// We expect blocks that are within the difficulty adjustment window size of
|
||||
// the pruning point and its anticone to not get pruned
|
||||
unprunedBlockHashesBelowPruningPoint := make(map[externalapi.DomainHash]struct{})
|
||||
pruningPointAndItsAnticone, err := tc.PruningPointAndItsAnticoneWithTrustedData()
|
||||
pruningPointAndItsAnticone, err := tc.PruningPointAndItsAnticone()
|
||||
if err != nil {
|
||||
t.Fatalf("pruningPointAndItsAnticone: %+v", err)
|
||||
}
|
||||
for _, block := range pruningPointAndItsAnticone {
|
||||
blockHash := consensushashing.BlockHash(block.Block)
|
||||
for _, blockHash := range pruningPointAndItsAnticone {
|
||||
unprunedBlockHashesBelowPruningPoint[*blockHash] = struct{}{}
|
||||
blockWindow, err := tc.DAGTraversalManager().BlockWindow(stagingArea, blockHash, consensusConfig.DifficultyAdjustmentWindowSize)
|
||||
if err != nil {
|
||||
|
||||
@@ -219,7 +219,7 @@ func (pm *pruningManager) nextPruningPointAndCandidateByBlockHash(stagingArea *m
|
||||
// We iterate until the selected parent of the given block, in order to allow a situation where the given block hash
|
||||
// belongs to the virtual. This shouldn't change anything since the max blue score difference between a block and its
|
||||
// selected parent is K, and K << pm.pruningDepth.
|
||||
iterator, err := pm.dagTraversalManager.SelectedChildIterator(stagingArea, ghostdagData.SelectedParent(), lowHash)
|
||||
iterator, err := pm.dagTraversalManager.SelectedChildIterator(stagingArea, ghostdagData.SelectedParent(), lowHash, true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -675,7 +675,19 @@ func (pm *pruningManager) calculateDiffBetweenPreviousAndCurrentPruningPoints(st
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "pruningManager.calculateDiffBetweenPreviousAndCurrentPruningPoints")
|
||||
defer onEnd()
|
||||
if currentPruningHash.Equal(pm.genesisHash) {
|
||||
return utxo.NewUTXODiff(), nil
|
||||
iter, err := pm.consensusStateManager.RestorePastUTXOSetIterator(stagingArea, currentPruningHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
set := make(map[externalapi.DomainOutpoint]externalapi.UTXOEntry)
|
||||
for ok := iter.First(); ok; ok = iter.Next() {
|
||||
outpoint, entry, err := iter.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
set[*outpoint] = entry
|
||||
}
|
||||
return utxo.NewUTXODiffFromCollections(utxo.NewUTXOCollection(set), utxo.NewUTXOCollection(make(map[externalapi.DomainOutpoint]externalapi.UTXOEntry)))
|
||||
}
|
||||
|
||||
pruningPointIndex, err := pm.pruningStore.CurrentPruningPointIndex(pm.databaseContext, stagingArea)
|
||||
@@ -907,8 +919,8 @@ func (pm *pruningManager) PruneAllBlocksBelow(stagingArea *model.StagingArea, pr
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pm *pruningManager) PruningPointAndItsAnticoneWithTrustedData() ([]*externalapi.BlockWithTrustedData, error) {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "PruningPointAndItsAnticoneWithTrustedData")
|
||||
func (pm *pruningManager) PruningPointAndItsAnticone() ([]*externalapi.DomainHash, error) {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "PruningPointAndItsAnticone")
|
||||
defer onEnd()
|
||||
|
||||
stagingArea := model.NewStagingArea()
|
||||
@@ -922,34 +934,32 @@ func (pm *pruningManager) PruningPointAndItsAnticoneWithTrustedData() ([]*extern
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blocks := make([]*externalapi.BlockWithTrustedData, 0, len(pruningPointAnticone)+1)
|
||||
|
||||
pruningPointWithTrustedData, err := pm.blockWithTrustedData(stagingArea, pruningPoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, blockHash := range pruningPointAnticone {
|
||||
blockWithTrustedData, err := pm.blockWithTrustedData(stagingArea, blockHash)
|
||||
// Sorting the blocks in topological order
|
||||
var sortErr error
|
||||
sort.Slice(pruningPointAnticone, func(i, j int) bool {
|
||||
headerI, err := pm.blockHeaderStore.BlockHeader(pm.databaseContext, stagingArea, pruningPointAnticone[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
sortErr = err
|
||||
return false
|
||||
}
|
||||
|
||||
blocks = append(blocks, blockWithTrustedData)
|
||||
headerJ, err := pm.blockHeaderStore.BlockHeader(pm.databaseContext, stagingArea, pruningPointAnticone[j])
|
||||
if err != nil {
|
||||
sortErr = err
|
||||
return false
|
||||
}
|
||||
|
||||
return headerI.BlueWork().Cmp(headerJ.BlueWork()) < 0
|
||||
})
|
||||
if sortErr != nil {
|
||||
return nil, sortErr
|
||||
}
|
||||
|
||||
// Sorting the blocks in topological order
|
||||
sort.Slice(blocks, func(i, j int) bool {
|
||||
return blocks[i].Block.Header.BlueWork().Cmp(blocks[j].Block.Header.BlueWork()) < 0
|
||||
})
|
||||
|
||||
// The pruning point should always come first
|
||||
blocks = append([]*externalapi.BlockWithTrustedData{pruningPointWithTrustedData}, blocks...)
|
||||
|
||||
return blocks, nil
|
||||
return append([]*externalapi.DomainHash{pruningPoint}, pruningPointAnticone...), nil
|
||||
}
|
||||
|
||||
func (pm *pruningManager) blockWithTrustedData(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (*externalapi.BlockWithTrustedData, error) {
|
||||
func (pm *pruningManager) BlockWithTrustedData(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (*externalapi.BlockWithTrustedData, error) {
|
||||
block, err := pm.blocksStore.Block(pm.databaseContext, stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1041,7 +1051,19 @@ func (pm *pruningManager) ExpectedHeaderPruningPoint(stagingArea *model.StagingA
|
||||
}
|
||||
|
||||
nextOrCurrentPruningPoint := selectedParentHeader.PruningPoint()
|
||||
if pm.finalityScore(ghostdagData.BlueScore()) > pm.finalityScore(selectedParentPruningPointHeader.BlueScore()+pm.pruningDepth) {
|
||||
pruningPoint, err := pm.pruningStore.PruningPoint(pm.databaseContext, stagingArea)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the block doesn't have the pruning in its selected chain we know for sure that it can't trigger a pruning point
|
||||
// change (we check the selected parent to take care of the case where the block is the virtual which doesn't have reachability data).
|
||||
hasPruningPointInItsSelectedChain, err := pm.dagTopologyManager.IsInSelectedParentChainOf(stagingArea, pruningPoint, ghostdagData.SelectedParent())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if hasPruningPointInItsSelectedChain && pm.finalityScore(ghostdagData.BlueScore()) > pm.finalityScore(selectedParentPruningPointHeader.BlueScore()+pm.pruningDepth) {
|
||||
var suggestedLowHash *externalapi.DomainHash
|
||||
hasReachabilityData, err := pm.reachabilityDataStore.HasReachabilityData(pm.databaseContext, stagingArea, selectedParentHeader.PruningPoint())
|
||||
if err != nil {
|
||||
|
||||
5
domain/consensus/processes/pruningproofmanager/log.go
Normal file
5
domain/consensus/processes/pruningproofmanager/log.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package pruningproofmanager
|
||||
|
||||
import "github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
|
||||
var log = logger.RegisterSubSystem("PPMN")
|
||||
@@ -15,8 +15,8 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashset"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/pow"
|
||||
"github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/pkg/errors"
|
||||
"math/big"
|
||||
)
|
||||
@@ -28,6 +28,7 @@ type pruningProofManager struct {
|
||||
ghostdagManagers []model.GHOSTDAGManager
|
||||
reachabilityManagers []model.ReachabilityManager
|
||||
dagTraversalManagers []model.DAGTraversalManager
|
||||
parentsManager model.ParentsManager
|
||||
|
||||
ghostdagDataStores []model.GHOSTDAGDataStore
|
||||
pruningStore model.PruningStore
|
||||
@@ -39,6 +40,9 @@ type pruningProofManager struct {
|
||||
genesisHash *externalapi.DomainHash
|
||||
k externalapi.KType
|
||||
pruningProofM uint64
|
||||
|
||||
cachedPruningPoint *externalapi.DomainHash
|
||||
cachedProof *externalapi.PruningPointProof
|
||||
}
|
||||
|
||||
// New instantiates a new PruningManager
|
||||
@@ -49,6 +53,7 @@ func New(
|
||||
ghostdagManagers []model.GHOSTDAGManager,
|
||||
reachabilityManagers []model.ReachabilityManager,
|
||||
dagTraversalManagers []model.DAGTraversalManager,
|
||||
parentsManager model.ParentsManager,
|
||||
|
||||
ghostdagDataStores []model.GHOSTDAGDataStore,
|
||||
pruningStore model.PruningStore,
|
||||
@@ -68,6 +73,7 @@ func New(
|
||||
ghostdagManagers: ghostdagManagers,
|
||||
reachabilityManagers: reachabilityManagers,
|
||||
dagTraversalManagers: dagTraversalManagers,
|
||||
parentsManager: parentsManager,
|
||||
|
||||
ghostdagDataStores: ghostdagDataStores,
|
||||
pruningStore: pruningStore,
|
||||
@@ -83,6 +89,33 @@ func New(
|
||||
}
|
||||
|
||||
func (ppm *pruningProofManager) BuildPruningPointProof(stagingArea *model.StagingArea) (*externalapi.PruningPointProof, error) {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "BuildPruningPointProof")
|
||||
defer onEnd()
|
||||
|
||||
pruningPoint, err := ppm.pruningStore.PruningPoint(ppm.databaseContext, stagingArea)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ppm.cachedPruningPoint != nil && ppm.cachedPruningPoint.Equal(pruningPoint) {
|
||||
return ppm.cachedProof, nil
|
||||
}
|
||||
|
||||
proof, err := ppm.buildPruningPointProof(stagingArea)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ppm.cachedProof = proof
|
||||
ppm.cachedPruningPoint = pruningPoint
|
||||
|
||||
return proof, nil
|
||||
}
|
||||
|
||||
func (ppm *pruningProofManager) buildPruningPointProof(stagingArea *model.StagingArea) (*externalapi.PruningPointProof, error) {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "buildPruningPointProof")
|
||||
defer onEnd()
|
||||
|
||||
pruningPoint, err := ppm.pruningStore.PruningPoint(ppm.databaseContext, stagingArea)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -97,17 +130,33 @@ func (ppm *pruningProofManager) BuildPruningPointProof(stagingArea *model.Stagin
|
||||
return nil, err
|
||||
}
|
||||
|
||||
maxLevel := len(pruningPointHeader.Parents()) - 1
|
||||
maxLevel := len(ppm.parentsManager.Parents(pruningPointHeader)) - 1
|
||||
headersByLevel := make(map[int][]externalapi.BlockHeader)
|
||||
selectedTipByLevel := make([]*externalapi.DomainHash, maxLevel+1)
|
||||
pruningPointLevel := pow.BlockLevel(pruningPointHeader)
|
||||
pruningPointLevel := pruningPointHeader.BlockLevel()
|
||||
for blockLevel := maxLevel; blockLevel >= 0; blockLevel-- {
|
||||
var selectedTip *externalapi.DomainHash
|
||||
if blockLevel <= pruningPointLevel {
|
||||
selectedTip = pruningPoint
|
||||
} else {
|
||||
blockLevelParents := pruningPointHeader.ParentsAtLevel(blockLevel)
|
||||
selectedTip, err = ppm.ghostdagManagers[blockLevel].ChooseSelectedParent(stagingArea, []*externalapi.DomainHash(blockLevelParents)...)
|
||||
blockLevelParents := ppm.parentsManager.ParentsAtLevel(pruningPointHeader, blockLevel)
|
||||
selectedTipCandidates := make([]*externalapi.DomainHash, 0, len(blockLevelParents))
|
||||
|
||||
// In a pruned node, some pruning point parents might be missing, but we're guaranteed that its
|
||||
// selected parent is not missing.
|
||||
for _, parent := range blockLevelParents {
|
||||
_, err := ppm.ghostdagDataStores[blockLevel].Get(ppm.databaseContext, stagingArea, parent, false)
|
||||
if database.IsNotFoundError(err) {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
selectedTipCandidates = append(selectedTipCandidates, parent)
|
||||
}
|
||||
|
||||
selectedTip, err = ppm.ghostdagManagers[blockLevel].ChooseSelectedParent(stagingArea, selectedTipCandidates...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -248,6 +297,9 @@ func (ppm *pruningProofManager) blockAtDepth(stagingArea *model.StagingArea, gho
|
||||
}
|
||||
|
||||
func (ppm *pruningProofManager) ValidatePruningPointProof(pruningPointProof *externalapi.PruningPointProof) error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "ValidatePruningPointProof")
|
||||
defer onEnd()
|
||||
|
||||
stagingArea := model.NewStagingArea()
|
||||
|
||||
if len(pruningPointProof.Headers) == 0 {
|
||||
@@ -257,8 +309,8 @@ func (ppm *pruningProofManager) ValidatePruningPointProof(pruningPointProof *ext
|
||||
level0Headers := pruningPointProof.Headers[0]
|
||||
pruningPointHeader := level0Headers[len(level0Headers)-1]
|
||||
pruningPoint := consensushashing.HeaderHash(pruningPointHeader)
|
||||
pruningPointBlockLevel := pow.BlockLevel(pruningPointHeader)
|
||||
maxLevel := len(pruningPointHeader.Parents()) - 1
|
||||
pruningPointBlockLevel := pruningPointHeader.BlockLevel()
|
||||
maxLevel := len(ppm.parentsManager.Parents(pruningPointHeader)) - 1
|
||||
if maxLevel >= len(pruningPointProof.Headers) {
|
||||
return errors.Wrapf(ruleerrors.ErrPruningProofEmpty, "proof has only %d levels while pruning point "+
|
||||
"has parents from %d levels", len(pruningPointProof.Headers), maxLevel+1)
|
||||
@@ -300,15 +352,15 @@ func (ppm *pruningProofManager) ValidatePruningPointProof(pruningPointProof *ext
|
||||
var selectedTip *externalapi.DomainHash
|
||||
for i, header := range headers {
|
||||
blockHash := consensushashing.HeaderHash(header)
|
||||
if pow.BlockLevel(header) < blockLevel {
|
||||
if header.BlockLevel() < blockLevel {
|
||||
return errors.Wrapf(ruleerrors.ErrPruningProofWrongBlockLevel, "block %s level is %d when it's "+
|
||||
"expected to be at least %d", blockHash, pow.BlockLevel(header), blockLevel)
|
||||
"expected to be at least %d", blockHash, header.BlockLevel(), blockLevel)
|
||||
}
|
||||
|
||||
blockHeaderStore.Stage(stagingArea, blockHash, header)
|
||||
|
||||
var parents []*externalapi.DomainHash
|
||||
for _, parent := range header.ParentsAtLevel(blockLevel) {
|
||||
for _, parent := range ppm.parentsManager.ParentsAtLevel(header, blockLevel) {
|
||||
_, err := ghostdagDataStores[blockLevel].Get(ppm.databaseContext, stagingArea, parent, false)
|
||||
if database.IsNotFoundError(err) {
|
||||
continue
|
||||
@@ -377,7 +429,7 @@ func (ppm *pruningProofManager) ValidatePruningPointProof(pruningPointProof *ext
|
||||
}
|
||||
}
|
||||
|
||||
if !selectedTip.Equal(pruningPoint) && !pruningPointHeader.ParentsAtLevel(blockLevel).Contains(selectedTip) {
|
||||
if !selectedTip.Equal(pruningPoint) && !ppm.parentsManager.ParentsAtLevel(pruningPointHeader, blockLevel).Contains(selectedTip) {
|
||||
return errors.Wrapf(ruleerrors.ErrPruningProofMissesBlocksBelowPruningPoint, "the selected tip %s at "+
|
||||
"level %d is not a parent of the pruning point", selectedTip, blockLevel)
|
||||
}
|
||||
@@ -395,7 +447,7 @@ func (ppm *pruningProofManager) ValidatePruningPointProof(pruningPointProof *ext
|
||||
return errors.Wrapf(ruleerrors.ErrPruningProofSelectedTipIsNotThePruningPoint, "the pruning "+
|
||||
"proof selected tip %s at level %d is not the pruning point", selectedTip, blockLevel)
|
||||
}
|
||||
} else if !pruningPointHeader.ParentsAtLevel(blockLevel).Contains(selectedTip) {
|
||||
} else if !ppm.parentsManager.ParentsAtLevel(pruningPointHeader, blockLevel).Contains(selectedTip) {
|
||||
return errors.Wrapf(ruleerrors.ErrPruningProofSelectedTipNotParentOfPruningPoint, "the pruning "+
|
||||
"proof selected tip %s at level %d is not a parent of the of the pruning point on the same "+
|
||||
"level", selectedTip, blockLevel)
|
||||
@@ -554,19 +606,22 @@ func (ppm *pruningProofManager) dagProcesses(
|
||||
}
|
||||
|
||||
func (ppm *pruningProofManager) ApplyPruningPointProof(stagingArea *model.StagingArea, pruningPointProof *externalapi.PruningPointProof) error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "ApplyPruningPointProof")
|
||||
defer onEnd()
|
||||
|
||||
for blockLevel, headers := range pruningPointProof.Headers {
|
||||
var selectedTip *externalapi.DomainHash
|
||||
for i, header := range headers {
|
||||
blockHash := consensushashing.HeaderHash(header)
|
||||
if pow.BlockLevel(header) < blockLevel {
|
||||
if header.BlockLevel() < blockLevel {
|
||||
return errors.Wrapf(ruleerrors.ErrPruningProofWrongBlockLevel, "block %s level is %d when it's "+
|
||||
"expected to be at least %d", blockHash, pow.BlockLevel(header), blockLevel)
|
||||
"expected to be at least %d", blockHash, header.BlockLevel(), blockLevel)
|
||||
}
|
||||
|
||||
ppm.blockHeaderStore.Stage(stagingArea, blockHash, header)
|
||||
|
||||
var parents []*externalapi.DomainHash
|
||||
for _, parent := range header.ParentsAtLevel(blockLevel) {
|
||||
for _, parent := range ppm.parentsManager.ParentsAtLevel(header, blockLevel) {
|
||||
_, err := ppm.ghostdagDataStores[blockLevel].Get(ppm.databaseContext, stagingArea, parent, false)
|
||||
if database.IsNotFoundError(err) {
|
||||
continue
|
||||
|
||||
@@ -46,7 +46,7 @@ func (sm *syncManager) antiPastHashesBetween(stagingArea *model.StagingArea, low
|
||||
|
||||
// Collect all hashes by concatenating the merge-sets of all blocks between highHash and lowHash
|
||||
blockHashes := []*externalapi.DomainHash{}
|
||||
iterator, err := sm.dagTraversalManager.SelectedChildIterator(stagingArea, highHash, lowHash)
|
||||
iterator, err := sm.dagTraversalManager.SelectedChildIterator(stagingArea, highHash, lowHash, false)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -121,7 +121,7 @@ func (sm *syncManager) missingBlockBodyHashes(stagingArea *model.StagingArea, hi
|
||||
return nil, err
|
||||
}
|
||||
|
||||
selectedChildIterator, err := sm.dagTraversalManager.SelectedChildIterator(stagingArea, highHash, pruningPoint)
|
||||
selectedChildIterator, err := sm.dagTraversalManager.SelectedChildIterator(stagingArea, highHash, pruningPoint, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -366,7 +366,7 @@ func TestSigningTwoInputs(t *testing.T) {
|
||||
input.SignatureScript = signatureScript
|
||||
}
|
||||
|
||||
_, insertionResult, err := tc.AddBlock([]*externalapi.DomainHash{block3Hash}, nil, []*externalapi.DomainTransaction{tx})
|
||||
_, virtualChangeSet, err := tc.AddBlock([]*externalapi.DomainHash{block3Hash}, nil, []*externalapi.DomainTransaction{tx})
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
@@ -375,7 +375,7 @@ func TestSigningTwoInputs(t *testing.T) {
|
||||
TransactionID: *consensushashing.TransactionID(tx),
|
||||
Index: 0,
|
||||
}
|
||||
if !insertionResult.VirtualUTXODiff.ToAdd().Contains(txOutpoint) {
|
||||
if !virtualChangeSet.VirtualUTXODiff.ToAdd().Contains(txOutpoint) {
|
||||
t.Fatalf("tx was not accepted by the DAG")
|
||||
}
|
||||
})
|
||||
@@ -492,7 +492,7 @@ func TestSigningTwoInputsECDSA(t *testing.T) {
|
||||
input.SignatureScript = signatureScript
|
||||
}
|
||||
|
||||
_, insertionResult, err := tc.AddBlock([]*externalapi.DomainHash{block3Hash}, nil, []*externalapi.DomainTransaction{tx})
|
||||
_, virtualChangeSet, err := tc.AddBlock([]*externalapi.DomainHash{block3Hash}, nil, []*externalapi.DomainTransaction{tx})
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
@@ -501,7 +501,7 @@ func TestSigningTwoInputsECDSA(t *testing.T) {
|
||||
TransactionID: *consensushashing.TransactionID(tx),
|
||||
Index: 0,
|
||||
}
|
||||
if !insertionResult.VirtualUTXODiff.ToAdd().Contains(txOutpoint) {
|
||||
if !virtualChangeSet.VirtualUTXODiff.ToAdd().Contains(txOutpoint) {
|
||||
t.Fatalf("tx was not accepted by the DAG")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -46,7 +46,7 @@ func (tc *testConsensus) BuildBlockWithParents(parentHashes []*externalapi.Domai
|
||||
}
|
||||
|
||||
func (tc *testConsensus) AddBlock(parentHashes []*externalapi.DomainHash, coinbaseData *externalapi.DomainCoinbaseData,
|
||||
transactions []*externalapi.DomainTransaction) (*externalapi.DomainHash, *externalapi.BlockInsertionResult, error) {
|
||||
transactions []*externalapi.DomainTransaction) (*externalapi.DomainHash, *externalapi.VirtualChangeSet, error) {
|
||||
|
||||
// Require write lock because BuildBlockWithParents stages temporary data
|
||||
tc.lock.Lock()
|
||||
@@ -57,16 +57,16 @@ func (tc *testConsensus) AddBlock(parentHashes []*externalapi.DomainHash, coinba
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
blockInsertionResult, err := tc.blockProcessor.ValidateAndInsertBlock(block, true)
|
||||
virtualChangeSet, err := tc.blockProcessor.ValidateAndInsertBlock(block, true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return consensushashing.BlockHash(block), blockInsertionResult, nil
|
||||
return consensushashing.BlockHash(block), virtualChangeSet, nil
|
||||
}
|
||||
|
||||
func (tc *testConsensus) AddUTXOInvalidHeader(parentHashes []*externalapi.DomainHash) (*externalapi.DomainHash,
|
||||
*externalapi.BlockInsertionResult, error) {
|
||||
*externalapi.VirtualChangeSet, error) {
|
||||
|
||||
// Require write lock because BuildBlockWithParents stages temporary data
|
||||
tc.lock.Lock()
|
||||
@@ -77,7 +77,7 @@ func (tc *testConsensus) AddUTXOInvalidHeader(parentHashes []*externalapi.Domain
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
blockInsertionResult, err := tc.blockProcessor.ValidateAndInsertBlock(&externalapi.DomainBlock{
|
||||
virtualChangeSet, err := tc.blockProcessor.ValidateAndInsertBlock(&externalapi.DomainBlock{
|
||||
Header: header,
|
||||
Transactions: nil,
|
||||
}, true)
|
||||
@@ -85,11 +85,11 @@ func (tc *testConsensus) AddUTXOInvalidHeader(parentHashes []*externalapi.Domain
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return consensushashing.HeaderHash(header), blockInsertionResult, nil
|
||||
return consensushashing.HeaderHash(header), virtualChangeSet, nil
|
||||
}
|
||||
|
||||
func (tc *testConsensus) AddUTXOInvalidBlock(parentHashes []*externalapi.DomainHash) (*externalapi.DomainHash,
|
||||
*externalapi.BlockInsertionResult, error) {
|
||||
*externalapi.VirtualChangeSet, error) {
|
||||
|
||||
// Require write lock because BuildBlockWithParents stages temporary data
|
||||
tc.lock.Lock()
|
||||
@@ -100,12 +100,12 @@ func (tc *testConsensus) AddUTXOInvalidBlock(parentHashes []*externalapi.DomainH
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
blockInsertionResult, err := tc.blockProcessor.ValidateAndInsertBlock(block, true)
|
||||
virtualChangeSet, err := tc.blockProcessor.ValidateAndInsertBlock(block, true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return consensushashing.BlockHash(block), blockInsertionResult, nil
|
||||
return consensushashing.BlockHash(block), virtualChangeSet, nil
|
||||
}
|
||||
|
||||
func (tc *testConsensus) MineJSON(r io.Reader, blockType testapi.MineJSONBlockType) (tips []*externalapi.DomainHash, err error) {
|
||||
|
||||
@@ -49,7 +49,7 @@ func TestCheckLockTimeVerifyConditionedByDAAScore(t *testing.T) {
|
||||
}
|
||||
fees := uint64(1)
|
||||
//Create a CLTV script:
|
||||
targetDAAScore := uint64(30)
|
||||
targetDAAScore := consensusConfig.GenesisBlock.Header.DAAScore() + uint64(30)
|
||||
redeemScriptCLTV, err := createScriptCLTV(targetDAAScore)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a script using createScriptCLTV: %v", err)
|
||||
@@ -156,7 +156,7 @@ func TestCheckLockTimeVerifyConditionedByDAAScoreWithWrongLockTime(t *testing.T)
|
||||
}
|
||||
fees := uint64(1)
|
||||
//Create a CLTV script:
|
||||
targetDAAScore := uint64(30)
|
||||
targetDAAScore := consensusConfig.GenesisBlock.Header.DAAScore() + uint64(30)
|
||||
redeemScriptCLTV, err := createScriptCLTV(targetDAAScore)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a script using createScriptCLTV: %v", err)
|
||||
@@ -313,7 +313,7 @@ func TestCheckLockTimeVerifyConditionedByAbsoluteTime(t *testing.T) {
|
||||
for i := int64(0); ; i++ {
|
||||
tipBlock, err := testConsensus.BuildBlock(&emptyCoinbase, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating tip using BuildBlock: %v", err)
|
||||
t.Fatalf("Error creating tip using BuildBlock: %+v", err)
|
||||
}
|
||||
blockHeader := tipBlock.Header.ToMutable()
|
||||
blockHeader.SetTimeInMilliseconds(timeStampBlockE + i*1000)
|
||||
|
||||
@@ -2,6 +2,7 @@ package blockheader
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/pow"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
@@ -18,6 +19,9 @@ type blockHeader struct {
|
||||
blueScore uint64
|
||||
blueWork *big.Int
|
||||
pruningPoint *externalapi.DomainHash
|
||||
|
||||
isBlockLevelCached bool
|
||||
blockLevel int
|
||||
}
|
||||
|
||||
func (bh *blockHeader) BlueScore() uint64 {
|
||||
@@ -41,10 +45,12 @@ func (bh *blockHeader) ToImmutable() externalapi.BlockHeader {
|
||||
}
|
||||
|
||||
func (bh *blockHeader) SetNonce(nonce uint64) {
|
||||
bh.isBlockLevelCached = false
|
||||
bh.nonce = nonce
|
||||
}
|
||||
|
||||
func (bh *blockHeader) SetTimeInMilliseconds(timeInMilliseconds int64) {
|
||||
bh.isBlockLevelCached = false
|
||||
bh.timeInMilliseconds = timeInMilliseconds
|
||||
}
|
||||
|
||||
@@ -56,16 +62,12 @@ func (bh *blockHeader) Parents() []externalapi.BlockLevelParents {
|
||||
return bh.parents
|
||||
}
|
||||
|
||||
func (bh *blockHeader) ParentsAtLevel(level int) externalapi.BlockLevelParents {
|
||||
if len(bh.parents) <= level {
|
||||
func (bh *blockHeader) DirectParents() externalapi.BlockLevelParents {
|
||||
if len(bh.parents) == 0 {
|
||||
return externalapi.BlockLevelParents{}
|
||||
}
|
||||
|
||||
return bh.parents[level]
|
||||
}
|
||||
|
||||
func (bh *blockHeader) DirectParents() externalapi.BlockLevelParents {
|
||||
return bh.ParentsAtLevel(0)
|
||||
return bh.parents[0]
|
||||
}
|
||||
|
||||
func (bh *blockHeader) HashMerkleRoot() *externalapi.DomainHash {
|
||||
@@ -177,6 +179,15 @@ func (bh *blockHeader) ToMutable() externalapi.MutableBlockHeader {
|
||||
return bh.clone()
|
||||
}
|
||||
|
||||
func (bh *blockHeader) BlockLevel() int {
|
||||
if !bh.isBlockLevelCached {
|
||||
bh.blockLevel = pow.BlockLevel(bh)
|
||||
bh.isBlockLevelCached = true
|
||||
}
|
||||
|
||||
return bh.blockLevel
|
||||
}
|
||||
|
||||
// NewImmutableBlockHeader returns a new immutable header
|
||||
func NewImmutableBlockHeader(
|
||||
version uint16,
|
||||
|
||||
@@ -36,6 +36,8 @@ func TestDomainBlockHeader_Equal(t *testing.T) {
|
||||
8,
|
||||
big.NewInt(9),
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{10}),
|
||||
false,
|
||||
0,
|
||||
},
|
||||
expectedResult: false,
|
||||
},
|
||||
@@ -55,6 +57,8 @@ func TestDomainBlockHeader_Equal(t *testing.T) {
|
||||
9,
|
||||
big.NewInt(10),
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{11}),
|
||||
false,
|
||||
0,
|
||||
},
|
||||
headersToCompareTo: []headerToCompare{
|
||||
{
|
||||
@@ -75,6 +79,8 @@ func TestDomainBlockHeader_Equal(t *testing.T) {
|
||||
9,
|
||||
big.NewInt(10),
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{11}),
|
||||
false,
|
||||
0,
|
||||
},
|
||||
expectedResult: true,
|
||||
},
|
||||
@@ -92,6 +98,8 @@ func TestDomainBlockHeader_Equal(t *testing.T) {
|
||||
9,
|
||||
big.NewInt(10),
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{11}),
|
||||
false,
|
||||
0,
|
||||
},
|
||||
expectedResult: false,
|
||||
},
|
||||
@@ -111,6 +119,8 @@ func TestDomainBlockHeader_Equal(t *testing.T) {
|
||||
9,
|
||||
big.NewInt(10),
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{11}),
|
||||
false,
|
||||
0,
|
||||
},
|
||||
expectedResult: false,
|
||||
},
|
||||
@@ -128,6 +138,8 @@ func TestDomainBlockHeader_Equal(t *testing.T) {
|
||||
9,
|
||||
big.NewInt(10),
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{11}),
|
||||
false,
|
||||
0,
|
||||
},
|
||||
expectedResult: false,
|
||||
},
|
||||
@@ -145,6 +157,8 @@ func TestDomainBlockHeader_Equal(t *testing.T) {
|
||||
9,
|
||||
big.NewInt(10),
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{11}),
|
||||
false,
|
||||
0,
|
||||
},
|
||||
expectedResult: false,
|
||||
},
|
||||
@@ -162,6 +176,8 @@ func TestDomainBlockHeader_Equal(t *testing.T) {
|
||||
9,
|
||||
big.NewInt(10),
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{11}),
|
||||
false,
|
||||
0,
|
||||
},
|
||||
expectedResult: false,
|
||||
},
|
||||
@@ -179,6 +195,8 @@ func TestDomainBlockHeader_Equal(t *testing.T) {
|
||||
9,
|
||||
big.NewInt(10),
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{11}),
|
||||
false,
|
||||
0,
|
||||
},
|
||||
expectedResult: false,
|
||||
},
|
||||
@@ -196,6 +214,8 @@ func TestDomainBlockHeader_Equal(t *testing.T) {
|
||||
9,
|
||||
big.NewInt(10),
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{11}),
|
||||
false,
|
||||
0,
|
||||
},
|
||||
expectedResult: false,
|
||||
},
|
||||
@@ -213,6 +233,8 @@ func TestDomainBlockHeader_Equal(t *testing.T) {
|
||||
9,
|
||||
big.NewInt(10),
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{11}),
|
||||
false,
|
||||
0,
|
||||
},
|
||||
expectedResult: false,
|
||||
},
|
||||
@@ -230,6 +252,8 @@ func TestDomainBlockHeader_Equal(t *testing.T) {
|
||||
9,
|
||||
big.NewInt(10),
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{11}),
|
||||
false,
|
||||
0,
|
||||
},
|
||||
expectedResult: false,
|
||||
},
|
||||
@@ -247,6 +271,8 @@ func TestDomainBlockHeader_Equal(t *testing.T) {
|
||||
9,
|
||||
big.NewInt(10),
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{11}),
|
||||
false,
|
||||
0,
|
||||
},
|
||||
expectedResult: false,
|
||||
},
|
||||
@@ -264,6 +290,8 @@ func TestDomainBlockHeader_Equal(t *testing.T) {
|
||||
100,
|
||||
big.NewInt(10),
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{11}),
|
||||
false,
|
||||
0,
|
||||
},
|
||||
expectedResult: false,
|
||||
},
|
||||
@@ -281,6 +309,8 @@ func TestDomainBlockHeader_Equal(t *testing.T) {
|
||||
9,
|
||||
big.NewInt(100),
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{11}),
|
||||
false,
|
||||
0,
|
||||
},
|
||||
expectedResult: false,
|
||||
},
|
||||
@@ -298,6 +328,8 @@ func TestDomainBlockHeader_Equal(t *testing.T) {
|
||||
9,
|
||||
big.NewInt(10),
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{100}),
|
||||
false,
|
||||
0,
|
||||
},
|
||||
expectedResult: false,
|
||||
},
|
||||
|
||||
@@ -37,5 +37,7 @@ const (
|
||||
LockTimeThreshold = 5e11 // Tue Nov 5 00:53:20 1985 UTC
|
||||
|
||||
// MaxBlockLevel is the maximum possible block level.
|
||||
MaxBlockLevel = 255
|
||||
// This is technically 255, but we clamped it at 256 - block level of mainnet genesis
|
||||
// This means that any block that has a level lower or equal to genesis will be level 0.
|
||||
MaxBlockLevel = 225
|
||||
)
|
||||
|
||||
@@ -6,18 +6,17 @@ import (
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/pow"
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// SolveBlock increments the given block's nonce until it matches the difficulty requirements in its bits field
|
||||
func SolveBlock(block *externalapi.DomainBlock, rd *rand.Rand) {
|
||||
targetDifficulty := difficulty.CompactToBig(block.Header.Bits())
|
||||
headerForMining := block.Header.ToMutable()
|
||||
for i := rd.Uint64(); i < math.MaxUint64; i++ {
|
||||
headerForMining.SetNonce(i)
|
||||
if pow.CheckProofOfWorkWithTarget(headerForMining, targetDifficulty) {
|
||||
block.Header = headerForMining.ToImmutable()
|
||||
header := block.Header.ToMutable()
|
||||
state := pow.NewState(header)
|
||||
for state.Nonce = rd.Uint64(); state.Nonce < math.MaxUint64; state.Nonce++ {
|
||||
if state.CheckProofOfWork() {
|
||||
header.SetNonce(state.Nonce)
|
||||
block.Header = header.ToImmutable()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ func (mat *matrix) computeRank() int {
|
||||
var rowSelected [64]bool
|
||||
for i := 0; i < 64; i++ {
|
||||
var j int
|
||||
for j := 0; j < 64; j++ {
|
||||
for j = 0; j < 64; j++ {
|
||||
if !rowSelected[j] && math.Abs(B[j][i]) > eps {
|
||||
break
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user