mirror of
https://github.com/kaspanet/kaspad.git
synced 2026-03-22 16:13:45 +00:00
Compare commits
2 Commits
nod-1429-w
...
v0.8.9-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b8eda2226 | ||
|
|
15785da628 |
1
.github/workflows/go-race.yml
vendored
1
.github/workflows/go-race.yml
vendored
@@ -9,7 +9,6 @@ jobs:
|
||||
race_test:
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
branch: [ master, latest ]
|
||||
name: Race detection on ${{ matrix.branch }}
|
||||
|
||||
9
.github/workflows/go.yml
vendored
9
.github/workflows/go.yml
vendored
@@ -11,7 +11,6 @@ jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ ubuntu-16.04, macos-10.15 ]
|
||||
name: Testing on on ${{ matrix.os }}
|
||||
@@ -35,7 +34,7 @@ jobs:
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
go-version: 1.15
|
||||
|
||||
|
||||
# Source: https://github.com/actions/cache/blob/main/examples.md#go---modules
|
||||
@@ -49,7 +48,7 @@ jobs:
|
||||
|
||||
- name: Test
|
||||
shell: bash
|
||||
run: ./build_and_test.sh -v
|
||||
run: ./build_and_test.sh
|
||||
|
||||
coverage:
|
||||
runs-on: ubuntu-20.04
|
||||
@@ -61,10 +60,10 @@ jobs:
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
go-version: 1.15
|
||||
|
||||
- name: Create coverage file
|
||||
run: go test -v -covermode=atomic -coverpkg=./... -coverprofile coverage.txt ./...
|
||||
run: go test -covermode=atomic -coverpkg=./... -coverprofile coverage.txt ./...
|
||||
|
||||
- name: Upload coverage file
|
||||
run: bash <(curl -s https://codecov.io/bash)
|
||||
@@ -18,7 +18,7 @@ Kaspa is an attempt at a proof-of-work cryptocurrency with instant confirmations
|
||||
|
||||
## Requirements
|
||||
|
||||
Go 1.16 or later.
|
||||
Go 1.15 or later.
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package flowcontext
|
||||
|
||||
import (
|
||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
@@ -99,10 +98,6 @@ func (f *FlowContext) SharedRequestedBlocks() *blockrelay.SharedRequestedBlocks
|
||||
|
||||
// AddBlock adds the given block to the DAG and propagates it.
|
||||
func (f *FlowContext) AddBlock(block *externalapi.DomainBlock) error {
|
||||
if len(block.Transactions) == 0 {
|
||||
return protocolerrors.Errorf(false, "cannot add header only block")
|
||||
}
|
||||
|
||||
blockInsertionResult, err := f.Domain().Consensus().ValidateAndInsertBlock(block)
|
||||
if err != nil {
|
||||
if errors.As(err, &ruleerrors.RuleError{}) {
|
||||
|
||||
@@ -104,11 +104,6 @@ func (flow *handleRelayInvsFlow) start() error {
|
||||
continue
|
||||
}
|
||||
|
||||
err = flow.banIfBlockIsHeaderOnly(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("Processing block %s", inv.Hash)
|
||||
missingParents, blockInsertionResult, err := flow.processBlock(block)
|
||||
if err != nil {
|
||||
@@ -145,15 +140,6 @@ func (flow *handleRelayInvsFlow) start() error {
|
||||
}
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) banIfBlockIsHeaderOnly(block *externalapi.DomainBlock) error {
|
||||
if len(block.Transactions) == 0 {
|
||||
return protocolerrors.Errorf(true, "sent header of %s block where expected block with body",
|
||||
consensushashing.BlockHash(block))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) readInv() (*appmessage.MsgInvRelayBlock, error) {
|
||||
if len(flow.invsQueue) > 0 {
|
||||
var inv *appmessage.MsgInvRelayBlock
|
||||
|
||||
@@ -533,11 +533,6 @@ func (flow *handleRelayInvsFlow) syncMissingBlockBodies(highHash *externalapi.Do
|
||||
return protocolerrors.Errorf(true, "expected block %s but got %s", expectedHash, blockHash)
|
||||
}
|
||||
|
||||
err = flow.banIfBlockIsHeaderOnly(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blockInsertionResult, err := flow.Domain().Consensus().ValidateAndInsertBlock(block)
|
||||
if err != nil {
|
||||
if errors.Is(err, ruleerrors.ErrDuplicateBlock) {
|
||||
|
||||
@@ -24,19 +24,6 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var headerOnlyBlock = &externalapi.DomainBlock{
|
||||
Header: blockheader.NewImmutableBlockHeader(
|
||||
constants.MaxBlockVersion,
|
||||
[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})},
|
||||
&externalapi.DomainHash{},
|
||||
&externalapi.DomainHash{},
|
||||
&externalapi.DomainHash{},
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
),
|
||||
}
|
||||
|
||||
var orphanBlock = &externalapi.DomainBlock{
|
||||
Header: blockheader.NewImmutableBlockHeader(
|
||||
constants.MaxBlockVersion,
|
||||
@@ -48,7 +35,6 @@ var orphanBlock = &externalapi.DomainBlock{
|
||||
0,
|
||||
0,
|
||||
),
|
||||
Transactions: []*externalapi.DomainTransaction{{}},
|
||||
}
|
||||
|
||||
var validPruningPointBlock = &externalapi.DomainBlock{
|
||||
@@ -62,7 +48,6 @@ var validPruningPointBlock = &externalapi.DomainBlock{
|
||||
0,
|
||||
0,
|
||||
),
|
||||
Transactions: []*externalapi.DomainTransaction{{}},
|
||||
}
|
||||
|
||||
var invalidPruningPointBlock = &externalapi.DomainBlock{
|
||||
@@ -76,7 +61,6 @@ var invalidPruningPointBlock = &externalapi.DomainBlock{
|
||||
0,
|
||||
0,
|
||||
),
|
||||
Transactions: []*externalapi.DomainTransaction{{}},
|
||||
}
|
||||
|
||||
var unexpectedIBDBlock = &externalapi.DomainBlock{
|
||||
@@ -90,7 +74,6 @@ var unexpectedIBDBlock = &externalapi.DomainBlock{
|
||||
0,
|
||||
0,
|
||||
),
|
||||
Transactions: []*externalapi.DomainTransaction{{}},
|
||||
}
|
||||
|
||||
var invalidBlock = &externalapi.DomainBlock{
|
||||
@@ -104,7 +87,6 @@ var invalidBlock = &externalapi.DomainBlock{
|
||||
0,
|
||||
0,
|
||||
),
|
||||
Transactions: []*externalapi.DomainTransaction{{}},
|
||||
}
|
||||
|
||||
var unknownBlockHash = externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})
|
||||
@@ -113,7 +95,6 @@ var validPruningPointHash = consensushashing.BlockHash(validPruningPointBlock)
|
||||
var invalidBlockHash = consensushashing.BlockHash(invalidBlock)
|
||||
var invalidPruningPointHash = consensushashing.BlockHash(invalidPruningPointBlock)
|
||||
var orphanBlockHash = consensushashing.BlockHash(orphanBlock)
|
||||
var headerOnlyBlockHash = consensushashing.BlockHash(headerOnlyBlock)
|
||||
|
||||
type fakeRelayInvsContext struct {
|
||||
testName string
|
||||
@@ -469,29 +450,6 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
expectsBan: true,
|
||||
expectsErrToContain: "got unrequested block",
|
||||
},
|
||||
{
|
||||
name: "sending header only block on relay",
|
||||
funcToExecute: func(t *testing.T, incomingRoute, outgoingRoute *router.Route, context *fakeRelayInvsContext) {
|
||||
err := incomingRoute.Enqueue(appmessage.NewMsgInvBlock(headerOnlyBlockHash))
|
||||
if err != nil {
|
||||
t.Fatalf("Enqueue: %+v", err)
|
||||
}
|
||||
|
||||
msg, err := outgoingRoute.DequeueWithTimeout(time.Second)
|
||||
if err != nil {
|
||||
t.Fatalf("DequeueWithTimeout: %+v", err)
|
||||
}
|
||||
_ = msg.(*appmessage.MsgRequestRelayBlocks)
|
||||
|
||||
err = incomingRoute.Enqueue(appmessage.DomainBlockToMsgBlock(headerOnlyBlock))
|
||||
if err != nil {
|
||||
t.Fatalf("Enqueue: %+v", err)
|
||||
}
|
||||
},
|
||||
expectsProtocolError: true,
|
||||
expectsBan: true,
|
||||
expectsErrToContain: "block where expected block with body",
|
||||
},
|
||||
{
|
||||
name: "sending invalid block",
|
||||
funcToExecute: func(t *testing.T, incomingRoute, outgoingRoute *router.Route, context *fakeRelayInvsContext) {
|
||||
|
||||
@@ -2,7 +2,6 @@ package rpchandlers
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
@@ -26,10 +25,9 @@ func HandleSubmitBlock(context *rpccontext.Context, _ *router.Router, request ap
|
||||
|
||||
err := context.ProtocolManager.AddBlock(domainBlock)
|
||||
if err != nil {
|
||||
if !errors.As(err, &ruleerrors.RuleError{}) || !errors.As(err, &protocolerrors.ProtocolError{}) {
|
||||
if !errors.As(err, &ruleerrors.RuleError{}) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &appmessage.SubmitBlockResponseMessage{
|
||||
Error: appmessage.RPCErrorf("Block rejected. Reason: %s", err),
|
||||
RejectReason: appmessage.RejectReasonBlockInvalid,
|
||||
|
||||
@@ -4,7 +4,7 @@ kaspactl is an RPC client for kaspad
|
||||
|
||||
## Requirements
|
||||
|
||||
Go 1.16 or later.
|
||||
Go 1.15 or later.
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -- multistage docker build: stage #1: build stage
|
||||
FROM golang:1.16-alpine AS build
|
||||
FROM golang:1.15-alpine AS build
|
||||
|
||||
RUN mkdir -p /go/src/github.com/kaspanet/kaspad
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ Kaspaminer is a CPU-based miner for kaspad
|
||||
|
||||
## Requirements
|
||||
|
||||
Go 1.16 or later.
|
||||
Go 1.15 or later.
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -- multistage docker build: stage #1: build stage
|
||||
FROM golang:1.16-alpine AS build
|
||||
FROM golang:1.15-alpine AS build
|
||||
|
||||
RUN mkdir -p /go/src/github.com/kaspanet/kaspad
|
||||
|
||||
|
||||
@@ -2,13 +2,12 @@ package main
|
||||
|
||||
import (
|
||||
nativeerrors "errors"
|
||||
"math/rand"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/cmd/kaspaminer/templatemanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/pow"
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
"math/rand"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
|
||||
@@ -156,15 +155,11 @@ func mineNextBlock(mineWhenNotSynced bool) *externalapi.DomainBlock {
|
||||
|
||||
func getBlockForMining(mineWhenNotSynced bool) *externalapi.DomainBlock {
|
||||
tryCount := 0
|
||||
|
||||
const sleepTime = 500 * time.Millisecond
|
||||
const sleepTimeWhenNotSynced = 5 * time.Second
|
||||
|
||||
for {
|
||||
tryCount++
|
||||
|
||||
const sleepTime = 500 * time.Millisecond
|
||||
shouldLog := (tryCount-1)%10 == 0
|
||||
template, isSynced := templatemanager.Get()
|
||||
template := templatemanager.Get()
|
||||
if template == nil {
|
||||
if shouldLog {
|
||||
log.Info("Waiting for the initial template")
|
||||
@@ -172,15 +167,15 @@ func getBlockForMining(mineWhenNotSynced bool) *externalapi.DomainBlock {
|
||||
time.Sleep(sleepTime)
|
||||
continue
|
||||
}
|
||||
if !isSynced && !mineWhenNotSynced {
|
||||
if !template.IsSynced && !mineWhenNotSynced {
|
||||
if shouldLog {
|
||||
log.Warnf("Kaspad is not synced. Skipping current block template")
|
||||
}
|
||||
time.Sleep(sleepTimeWhenNotSynced)
|
||||
time.Sleep(sleepTime)
|
||||
continue
|
||||
}
|
||||
|
||||
return template
|
||||
return appmessage.MsgBlockToDomainBlock(template.MsgBlock)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,31 +2,22 @@ package templatemanager
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var currentTemplate *externalapi.DomainBlock
|
||||
var isSynced bool
|
||||
var currentTemplate *appmessage.GetBlockTemplateResponseMessage
|
||||
var lock = &sync.Mutex{}
|
||||
|
||||
// Get returns the template to work on
|
||||
func Get() (*externalapi.DomainBlock, bool) {
|
||||
func Get() *appmessage.GetBlockTemplateResponseMessage {
|
||||
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
|
||||
}
|
||||
block := *currentTemplate
|
||||
return &block, isSynced
|
||||
return currentTemplate
|
||||
}
|
||||
|
||||
// Set sets the current template to work on
|
||||
func Set(template *appmessage.GetBlockTemplateResponseMessage) {
|
||||
block := appmessage.MsgBlockToDomainBlock(template.MsgBlock)
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
currentTemplate = block
|
||||
isSynced = template.IsSynced
|
||||
currentTemplate = template
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ It is capable of generating wallet key-pairs, printing a wallet's current balanc
|
||||
|
||||
## Requirements
|
||||
|
||||
Go 1.16 or later.
|
||||
Go 1.15 or later.
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -- multistage docker build: stage #1: build stage
|
||||
FROM golang:1.16-alpine AS build
|
||||
FROM golang:1.15-alpine AS build
|
||||
|
||||
RUN mkdir -p /go/src/github.com/kaspanet/kaspad
|
||||
|
||||
@@ -16,12 +16,9 @@ RUN go get -u golang.org/x/lint/golint \
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
|
||||
# Cache kaspad dependencies
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN ./build_and_test.sh
|
||||
RUN NO_PARALLEL=1 ./build_and_test.sh
|
||||
|
||||
# --- multistage docker build: stage #2: runtime image
|
||||
FROM alpine
|
||||
|
||||
@@ -1,21 +1,6 @@
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"math/big"
|
||||
"time"
|
||||
)
|
||||
import "github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
|
||||
// GHOSTDAGManagerConstructor is the function signature for a constructor of a type implementing model.GHOSTDAGManager
|
||||
type GHOSTDAGManagerConstructor func(model.DBReader, model.DAGTopologyManager,
|
||||
model.GHOSTDAGDataStore, model.BlockHeaderStore, model.KType) model.GHOSTDAGManager
|
||||
|
||||
// DifficultyManagerConstructor is the function signature for a constructor of a type implementing model.DifficultyManager
|
||||
type DifficultyManagerConstructor func(model.DBReader, model.GHOSTDAGManager, model.GHOSTDAGDataStore,
|
||||
model.BlockHeaderStore, model.DAGTopologyManager, model.DAGTraversalManager, *big.Int, int, bool, time.Duration,
|
||||
*externalapi.DomainHash) model.DifficultyManager
|
||||
|
||||
// PastMedianTimeManagerConstructor is the function signature for a constructor of a type implementing model.PastMedianTimeManager
|
||||
type PastMedianTimeManagerConstructor func(int, model.DBReader, model.DAGTraversalManager, model.BlockHeaderStore,
|
||||
model.GHOSTDAGDataStore) model.PastMedianTimeManager
|
||||
type GHOSTDAGManagerConstructor func(model.DBReader, model.DAGTopologyManager, model.GHOSTDAGDataStore, model.BlockHeaderStore, model.KType) model.GHOSTDAGManager
|
||||
|
||||
@@ -63,25 +63,19 @@ type Factory interface {
|
||||
SetTestGHOSTDAGManager(ghostdagConstructor GHOSTDAGManagerConstructor)
|
||||
SetTestLevelDBCacheSize(cacheSizeMiB int)
|
||||
SetTestPreAllocateCache(preallocateCaches bool)
|
||||
SetTestPastMedianTimeManager(medianTimeConstructor PastMedianTimeManagerConstructor)
|
||||
SetTestDifficultyManager(difficultyConstructor DifficultyManagerConstructor)
|
||||
}
|
||||
|
||||
type factory struct {
|
||||
dataDir string
|
||||
ghostdagConstructor GHOSTDAGManagerConstructor
|
||||
pastMedianTimeConsructor PastMedianTimeManagerConstructor
|
||||
difficultyConstructor DifficultyManagerConstructor
|
||||
cacheSizeMiB *int
|
||||
preallocateCaches *bool
|
||||
dataDir string
|
||||
ghostdagConstructor GHOSTDAGManagerConstructor
|
||||
cacheSizeMiB *int
|
||||
preallocateCaches *bool
|
||||
}
|
||||
|
||||
// NewFactory creates a new Consensus factory
|
||||
func NewFactory() Factory {
|
||||
return &factory{
|
||||
ghostdagConstructor: ghostdagmanager.New,
|
||||
pastMedianTimeConsructor: pastmediantimemanager.New,
|
||||
difficultyConstructor: difficultymanager.New,
|
||||
ghostdagConstructor: ghostdagmanager.New,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +144,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
reachabilityDataStore,
|
||||
ghostdagManager,
|
||||
consensusStateStore)
|
||||
pastMedianTimeManager := f.pastMedianTimeConsructor(
|
||||
pastMedianTimeManager := pastmediantimemanager.New(
|
||||
dagParams.TimestampDeviationTolerance,
|
||||
dbManager,
|
||||
dagTraversalManager,
|
||||
@@ -165,7 +159,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
dbManager,
|
||||
pastMedianTimeManager,
|
||||
ghostdagDataStore)
|
||||
difficultyManager := f.difficultyConstructor(
|
||||
difficultyManager := difficultymanager.New(
|
||||
dbManager,
|
||||
ghostdagManager,
|
||||
ghostdagDataStore,
|
||||
@@ -472,15 +466,6 @@ func (f *factory) SetTestGHOSTDAGManager(ghostdagConstructor GHOSTDAGManagerCons
|
||||
f.ghostdagConstructor = ghostdagConstructor
|
||||
}
|
||||
|
||||
func (f *factory) SetTestPastMedianTimeManager(medianTimeConstructor PastMedianTimeManagerConstructor) {
|
||||
f.pastMedianTimeConsructor = medianTimeConstructor
|
||||
}
|
||||
|
||||
// SetTestDifficultyManager is a setter for the difficultyManager field on the factory.
|
||||
func (f *factory) SetTestDifficultyManager(difficultyConstructor DifficultyManagerConstructor) {
|
||||
f.difficultyConstructor = difficultyConstructor
|
||||
}
|
||||
|
||||
func (f *factory) SetTestLevelDBCacheSize(cacheSizeMiB int) {
|
||||
f.cacheSizeMiB = &cacheSizeMiB
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ type DAGTraversalManager interface {
|
||||
// from lowHash (exclusive) to highHash (inclusive) over highHash's selected parent chain
|
||||
SelectedChildIterator(highHash, lowHash *externalapi.DomainHash) (BlockIterator, error)
|
||||
Anticone(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error)
|
||||
BlockWindow(highHash *externalapi.DomainHash, windowSize int) ([]*externalapi.DomainHash, error)
|
||||
BlueWindow(highHash *externalapi.DomainHash, windowSize int) ([]*externalapi.DomainHash, error)
|
||||
NewDownHeap() BlockHeap
|
||||
NewUpHeap() BlockHeap
|
||||
CalculateChainPath(
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) 2015-2017 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blocklogger
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/util/mstime"
|
||||
)
|
||||
|
||||
// BlockLogger is a type tracking the amount of blocks/headers/transactions to log the time it took to receive them
|
||||
type BlockLogger struct {
|
||||
receivedLogBlocks int64
|
||||
receivedLogHeaders int64
|
||||
receivedLogTransactions int64
|
||||
lastBlockLogTime time.Time
|
||||
}
|
||||
|
||||
// NewBlockLogger creates a new instance with zeroed blocks/headers/transactions/time counters.
|
||||
func NewBlockLogger() *BlockLogger {
|
||||
return &BlockLogger{
|
||||
receivedLogBlocks: 0,
|
||||
receivedLogHeaders: 0,
|
||||
receivedLogTransactions: 0,
|
||||
lastBlockLogTime: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// LogBlock logs a new block blue score as an information message
|
||||
// to show progress to the user. In order to prevent spam, it limits logging to
|
||||
// one message every 10 seconds with duration and totals included.
|
||||
func (bl *BlockLogger) LogBlock(block *externalapi.DomainBlock) {
|
||||
if len(block.Transactions) == 0 {
|
||||
bl.receivedLogHeaders++
|
||||
} else {
|
||||
bl.receivedLogBlocks++
|
||||
}
|
||||
|
||||
bl.receivedLogTransactions += int64(len(block.Transactions))
|
||||
|
||||
now := time.Now()
|
||||
duration := now.Sub(bl.lastBlockLogTime)
|
||||
if duration < time.Second*10 {
|
||||
return
|
||||
}
|
||||
|
||||
// Truncate the duration to 10s of milliseconds.
|
||||
truncatedDuration := duration.Round(10 * time.Millisecond)
|
||||
|
||||
// Log information about new block blue score.
|
||||
blockStr := "blocks"
|
||||
if bl.receivedLogBlocks == 1 {
|
||||
blockStr = "block"
|
||||
}
|
||||
|
||||
txStr := "transactions"
|
||||
if bl.receivedLogTransactions == 1 {
|
||||
txStr = "transaction"
|
||||
}
|
||||
|
||||
headerStr := "headers"
|
||||
if bl.receivedLogBlocks == 1 {
|
||||
headerStr = "header"
|
||||
}
|
||||
|
||||
log.Infof("Processed %d %s and %d %s in the last %s (%d %s, %s)",
|
||||
bl.receivedLogBlocks, blockStr, bl.receivedLogHeaders, headerStr, truncatedDuration, bl.receivedLogTransactions,
|
||||
txStr, mstime.UnixMilliseconds(block.Header.TimeInMilliseconds()))
|
||||
|
||||
bl.receivedLogBlocks = 0
|
||||
bl.receivedLogHeaders = 0
|
||||
bl.receivedLogTransactions = 0
|
||||
bl.lastBlockLogTime = now
|
||||
}
|
||||
@@ -1,12 +1,6 @@
|
||||
package blockvalidator_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/merkle"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
@@ -90,7 +84,7 @@ func TestChainedTransactions(t *testing.T) {
|
||||
func TestCheckBlockSanity(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestCheckBlockSanity")
|
||||
consensus, teardown, err := factory.NewTestConsensus(params, false, "TestCheckBlockSanity")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
@@ -100,17 +94,17 @@ func TestCheckBlockSanity(t *testing.T) {
|
||||
t.Fatalf("Too few transactions in block, expect at least 3, got %v", len(exampleValidBlock.Transactions))
|
||||
}
|
||||
|
||||
tc.BlockStore().Stage(blockHash, &exampleValidBlock)
|
||||
consensus.BlockStore().Stage(blockHash, &exampleValidBlock)
|
||||
|
||||
err = tc.BlockValidator().ValidateBodyInIsolation(blockHash)
|
||||
err = consensus.BlockValidator().ValidateBodyInIsolation(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(blockHash, &blockWithWrongTxOrder)
|
||||
err = tc.BlockValidator().ValidateBodyInIsolation(blockHash)
|
||||
consensus.BlockStore().Stage(blockHash, &blockWithWrongTxOrder)
|
||||
err = consensus.BlockValidator().ValidateBodyInIsolation(blockHash)
|
||||
if !errors.Is(err, ruleerrors.ErrTransactionsNotSorted) {
|
||||
t.Errorf("CheckBlockSanity: Expected ErrTransactionsNotSorted error, instead got %v", err)
|
||||
}
|
||||
@@ -118,8 +112,8 @@ func TestCheckBlockSanity(t *testing.T) {
|
||||
// Test a block with invalid parents order
|
||||
// We no longer require blocks to have ordered parents
|
||||
blockHash = consensushashing.BlockHash(&unOrderedParentsBlock)
|
||||
tc.BlockStore().Stage(blockHash, &unOrderedParentsBlock)
|
||||
err = tc.BlockValidator().ValidateBodyInIsolation(blockHash)
|
||||
consensus.BlockStore().Stage(blockHash, &unOrderedParentsBlock)
|
||||
err = consensus.BlockValidator().ValidateBodyInIsolation(blockHash)
|
||||
if err != nil {
|
||||
t.Errorf("CheckBlockSanity: Expected block to be be body in isolation valid, got error instead: %v", err)
|
||||
}
|
||||
@@ -1040,309 +1034,3 @@ func TestCheckBlockHashMerkleRoot(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestBlockSize(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestBlockSize")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tc: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
block, _, err := initBlockWithInvalidBlockSize(params, tc)
|
||||
if err != nil {
|
||||
t.Fatalf("Error BuildBlockWithParents : %+v", err)
|
||||
}
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
tc.BlockStore().Stage(blockHash, block)
|
||||
|
||||
err = tc.BlockValidator().ValidateBodyInIsolation(blockHash)
|
||||
if err == nil || !errors.Is(err, ruleerrors.ErrBlockSizeTooHigh) {
|
||||
t.Fatalf("ValidateBodyInIsolationTest: TestBlockSize:"+
|
||||
" Unexpected error: Expected to: %v, but got : %v", ruleerrors.ErrBlockSizeTooHigh, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func initBlockWithInvalidBlockSize(params *dagconfig.Params, tc testapi.TestConsensus) (*externalapi.DomainBlock, model.UTXODiff, error) {
|
||||
emptyCoinbase := externalapi.DomainCoinbaseData{
|
||||
ScriptPublicKey: &externalapi.ScriptPublicKey{
|
||||
Script: nil,
|
||||
Version: 0,
|
||||
},
|
||||
}
|
||||
prevOutTxID := &externalapi.DomainTransactionID{}
|
||||
prevOutPoint := externalapi.DomainOutpoint{TransactionID: *prevOutTxID, Index: 1}
|
||||
bigSignatureScript := bytes.Repeat([]byte("01"), 25000)
|
||||
txInput := externalapi.DomainTransactionInput{
|
||||
PreviousOutpoint: prevOutPoint,
|
||||
SignatureScript: bigSignatureScript,
|
||||
Sequence: constants.MaxTxInSequenceNum,
|
||||
UTXOEntry: utxo.NewUTXOEntry(
|
||||
100_000_000,
|
||||
&externalapi.ScriptPublicKey{},
|
||||
true,
|
||||
uint64(5)),
|
||||
}
|
||||
tx := &externalapi.DomainTransaction{
|
||||
Version: constants.MaxTransactionVersion,
|
||||
Inputs: []*externalapi.DomainTransactionInput{&txInput},
|
||||
Outputs: []*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
|
||||
&externalapi.ScriptPublicKey{Script: []byte{1, 2}, Version: 0}}, {uint64(0xFFFF),
|
||||
&externalapi.ScriptPublicKey{Script: []byte{1, 3}, Version: 0}}},
|
||||
PayloadHash: *externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}),
|
||||
Payload: []byte{0x01},
|
||||
}
|
||||
|
||||
return tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, &emptyCoinbase, []*externalapi.DomainTransaction{tx})
|
||||
}
|
||||
|
||||
func TestCheckBlockDuplicateTransactions(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestCheckBlockDuplicateTransactions")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tc: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
block, _, err := initBlockWithDuplicateTransaction(params, tc)
|
||||
if err != nil {
|
||||
t.Fatalf("Error BuildBlockWithParents : %+v", err)
|
||||
}
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
tc.BlockStore().Stage(blockHash, block)
|
||||
|
||||
err = tc.BlockValidator().ValidateBodyInIsolation(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(params *dagconfig.Params, tc testapi.TestConsensus) (*externalapi.DomainBlock, model.UTXODiff, error) {
|
||||
emptyCoinbase := externalapi.DomainCoinbaseData{
|
||||
ScriptPublicKey: &externalapi.ScriptPublicKey{
|
||||
Script: nil,
|
||||
Version: 0,
|
||||
},
|
||||
}
|
||||
prevOutTxID := &externalapi.DomainTransactionID{}
|
||||
prevOutPoint := externalapi.DomainOutpoint{TransactionID: *prevOutTxID, Index: 1}
|
||||
txInput := externalapi.DomainTransactionInput{
|
||||
PreviousOutpoint: prevOutPoint,
|
||||
SignatureScript: bytes.Repeat([]byte("01"), 10),
|
||||
Sequence: constants.MaxTxInSequenceNum,
|
||||
UTXOEntry: utxo.NewUTXOEntry(
|
||||
100_000_000,
|
||||
&externalapi.ScriptPublicKey{},
|
||||
true,
|
||||
uint64(5)),
|
||||
}
|
||||
tx := &externalapi.DomainTransaction{
|
||||
Version: 0,
|
||||
Inputs: []*externalapi.DomainTransactionInput{&txInput},
|
||||
Outputs: []*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
|
||||
&externalapi.ScriptPublicKey{Script: []byte{1, 2}, Version: 0}}, {uint64(0xFFFF),
|
||||
&externalapi.ScriptPublicKey{Script: []byte{1, 3}, Version: 0}}},
|
||||
SubnetworkID: subnetworks.SubnetworkIDNative,
|
||||
}
|
||||
|
||||
return tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, &emptyCoinbase, []*externalapi.DomainTransaction{tx, tx})
|
||||
}
|
||||
|
||||
func TestCheckBlockContainsOnlyOneCoinbase(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestCheckBlockContainsOnlyOneCoinbase")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tc: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
block, _, err := initBlockWithMoreThanOneCoinbase(params, tc)
|
||||
if err != nil {
|
||||
t.Fatalf("Error BuildBlockWithParents : %+v", err)
|
||||
}
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
tc.BlockStore().Stage(blockHash, block)
|
||||
|
||||
err = tc.BlockValidator().ValidateBodyInIsolation(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(params *dagconfig.Params, tc testapi.TestConsensus) (*externalapi.DomainBlock, model.UTXODiff, error) {
|
||||
emptyCoinbase := externalapi.DomainCoinbaseData{
|
||||
ScriptPublicKey: &externalapi.ScriptPublicKey{
|
||||
Script: nil,
|
||||
Version: 0,
|
||||
},
|
||||
}
|
||||
prevOutTxID := &externalapi.DomainTransactionID{}
|
||||
prevOutPoint := externalapi.DomainOutpoint{TransactionID: *prevOutTxID, Index: 1}
|
||||
txInput := externalapi.DomainTransactionInput{
|
||||
PreviousOutpoint: prevOutPoint,
|
||||
SignatureScript: bytes.Repeat([]byte("01"), 10),
|
||||
Sequence: constants.MaxTxInSequenceNum,
|
||||
UTXOEntry: utxo.NewUTXOEntry(
|
||||
100_000_000,
|
||||
&externalapi.ScriptPublicKey{},
|
||||
true,
|
||||
uint64(5)),
|
||||
}
|
||||
tx := &externalapi.DomainTransaction{
|
||||
Version: 0,
|
||||
Inputs: []*externalapi.DomainTransactionInput{&txInput},
|
||||
Outputs: []*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
|
||||
&externalapi.ScriptPublicKey{Script: []byte{1, 2}, Version: 0}}, {uint64(0xFFFF),
|
||||
&externalapi.ScriptPublicKey{Script: []byte{1, 3}, Version: 0}}},
|
||||
SubnetworkID: subnetworks.SubnetworkIDCoinbase,
|
||||
}
|
||||
|
||||
return tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, &emptyCoinbase, []*externalapi.DomainTransaction{tx})
|
||||
}
|
||||
|
||||
func TestCheckBlockDoubleSpends(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestCheckBlockDoubleSpends")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tc: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
block, _, err := initBlockWithDoubleSpends(params, tc)
|
||||
if err != nil {
|
||||
t.Fatalf("Error BuildBlockWithParents : %+v", err)
|
||||
}
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
tc.BlockStore().Stage(blockHash, block)
|
||||
|
||||
err = tc.BlockValidator().ValidateBodyInIsolation(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(params *dagconfig.Params, tc testapi.TestConsensus) (*externalapi.DomainBlock, model.UTXODiff, error) {
|
||||
emptyCoinbase := externalapi.DomainCoinbaseData{
|
||||
ScriptPublicKey: &externalapi.ScriptPublicKey{
|
||||
Script: nil,
|
||||
Version: 0,
|
||||
},
|
||||
}
|
||||
prevOutTxID := &externalapi.DomainTransactionID{}
|
||||
prevOutPoint := externalapi.DomainOutpoint{TransactionID: *prevOutTxID, Index: 1}
|
||||
txInput := externalapi.DomainTransactionInput{
|
||||
PreviousOutpoint: prevOutPoint,
|
||||
SignatureScript: bytes.Repeat([]byte("01"), 10),
|
||||
Sequence: constants.MaxTxInSequenceNum,
|
||||
UTXOEntry: utxo.NewUTXOEntry(
|
||||
100_000_000,
|
||||
&externalapi.ScriptPublicKey{},
|
||||
true,
|
||||
uint64(5)),
|
||||
}
|
||||
tx := &externalapi.DomainTransaction{
|
||||
Version: 0,
|
||||
Inputs: []*externalapi.DomainTransactionInput{&txInput},
|
||||
Outputs: []*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
|
||||
&externalapi.ScriptPublicKey{Script: []byte{1, 2}, Version: 0}}, {uint64(0xFFFF),
|
||||
&externalapi.ScriptPublicKey{Script: []byte{1, 3}, Version: 0}}},
|
||||
SubnetworkID: subnetworks.SubnetworkIDNative,
|
||||
}
|
||||
txInputSameOutpoint := externalapi.DomainTransactionInput{
|
||||
PreviousOutpoint: prevOutPoint,
|
||||
SignatureScript: bytes.Repeat([]byte("02"), 10),
|
||||
Sequence: constants.MaxTxInSequenceNum,
|
||||
UTXOEntry: utxo.NewUTXOEntry(
|
||||
100_000_000,
|
||||
&externalapi.ScriptPublicKey{},
|
||||
true,
|
||||
uint64(4)),
|
||||
}
|
||||
txSameOutpoint := &externalapi.DomainTransaction{
|
||||
Version: 0,
|
||||
Inputs: []*externalapi.DomainTransactionInput{&txInputSameOutpoint},
|
||||
Outputs: []*externalapi.DomainTransactionOutput{{uint64(0xFF),
|
||||
&externalapi.ScriptPublicKey{Script: []byte{1, 2}, Version: 0}}, {uint64(0xFFFF),
|
||||
&externalapi.ScriptPublicKey{Script: []byte{1, 3}, Version: 0}}},
|
||||
SubnetworkID: subnetworks.SubnetworkIDNative,
|
||||
}
|
||||
|
||||
return tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash},
|
||||
&emptyCoinbase, []*externalapi.DomainTransaction{tx, txSameOutpoint})
|
||||
}
|
||||
|
||||
func TestCheckFirstBlockTransactionIsCoinbase(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestCheckFirstBlockTransactionIsCoinbase")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tc: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
block := initBlockWithFirstTransactionDifferentThanCoinbase(params)
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
tc.BlockStore().Stage(blockHash, block)
|
||||
|
||||
err = tc.BlockValidator().ValidateBodyInIsolation(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(params *dagconfig.Params) *externalapi.DomainBlock {
|
||||
prevOutTxID := &externalapi.DomainTransactionID{}
|
||||
prevOutPoint := externalapi.DomainOutpoint{TransactionID: *prevOutTxID, Index: 1}
|
||||
txInput := externalapi.DomainTransactionInput{
|
||||
PreviousOutpoint: prevOutPoint,
|
||||
SignatureScript: bytes.Repeat([]byte("01"), 10),
|
||||
Sequence: constants.MaxTxInSequenceNum,
|
||||
}
|
||||
tx := &externalapi.DomainTransaction{
|
||||
Version: 0,
|
||||
Inputs: []*externalapi.DomainTransactionInput{&txInput},
|
||||
Outputs: []*externalapi.DomainTransactionOutput{{uint64(0xFFFF),
|
||||
&externalapi.ScriptPublicKey{Script: []byte{1, 2}, Version: 0}}, {uint64(0xFFFF),
|
||||
&externalapi.ScriptPublicKey{Script: []byte{1, 3}, Version: 0}}},
|
||||
SubnetworkID: subnetworks.SubnetworkIDNative,
|
||||
}
|
||||
|
||||
return &externalapi.DomainBlock{
|
||||
Header: blockheader.NewImmutableBlockHeader(
|
||||
constants.MaxBlockVersion,
|
||||
[]*externalapi.DomainHash{params.GenesisHash},
|
||||
merkle.CalculateHashMerkleRoot([]*externalapi.DomainTransaction{tx}),
|
||||
&externalapi.DomainHash{},
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
|
||||
0x80, 0xf7, 0x00, 0xe3, 0x16, 0x3d, 0x04, 0x95,
|
||||
0x5b, 0x7e, 0xaf, 0x84, 0x7e, 0x1b, 0x6b, 0x06,
|
||||
0x4e, 0x06, 0xba, 0x64, 0xd7, 0x61, 0xda, 0x25,
|
||||
0x1a, 0x0e, 0x21, 0xd4, 0x64, 0x49, 0x02, 0xa2,
|
||||
}),
|
||||
0x5cd18053000,
|
||||
0x207fffff,
|
||||
0x1),
|
||||
Transactions: []*externalapi.DomainTransaction{tx},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package blockvalidator_test
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/pow"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
|
||||
@@ -13,8 +12,8 @@ import (
|
||||
"math"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
@@ -242,60 +241,3 @@ func TestCheckPruningPointViolation(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestValidateDifficulty verifies that in case of a block with an unexpected difficulty,
|
||||
// an appropriate error message (ErrUnexpectedDifficulty) will be returned on the
|
||||
// function ValidatePruningPointViolationAndProofOfWorkAndDifficulty. The required difficulty is
|
||||
// "calculated" by the function (dm *mocDifficultyManager) RequiredDifficulty ,
|
||||
// where mocDifficultyManager is special implementation of the type DifficultyManager for this test (defined below).
|
||||
func TestValidateDifficulty(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
mocDifficulty := &mocDifficultyManager{}
|
||||
factory.SetTestDifficultyManager(func(model.DBReader, model.GHOSTDAGManager, model.GHOSTDAGDataStore,
|
||||
model.BlockHeaderStore, model.DAGTopologyManager, model.DAGTraversalManager, *big.Int, int, bool, time.Duration,
|
||||
*externalapi.DomainHash) model.DifficultyManager {
|
||||
return mocDifficulty
|
||||
})
|
||||
genesisDifficulty := params.GenesisBlock.Header.Bits()
|
||||
mocDifficulty.testDifficulty = genesisDifficulty
|
||||
mocDifficulty.testGenesisBits = genesisDifficulty
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestValidateDifficulty")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
emptyCoinbase := externalapi.DomainCoinbaseData{
|
||||
ScriptPublicKey: &externalapi.ScriptPublicKey{
|
||||
Script: nil,
|
||||
Version: 0,
|
||||
},
|
||||
}
|
||||
block, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, &emptyCoinbase, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("TestValidateDifficulty: Failed build block with parents: %v.", err)
|
||||
}
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
tc.BlockStore().Stage(blockHash, block)
|
||||
tc.BlockHeaderStore().Stage(blockHash, block.Header)
|
||||
wrongTestDifficulty := mocDifficulty.testDifficulty + uint32(5)
|
||||
mocDifficulty.testDifficulty = wrongTestDifficulty
|
||||
|
||||
err = tc.BlockValidator().ValidatePruningPointViolationAndProofOfWorkAndDifficulty(blockHash)
|
||||
if err == nil || !errors.Is(err, ruleerrors.ErrUnexpectedDifficulty) {
|
||||
t.Fatalf("Expected block to be invalid with err: %v, instead found: %v", ruleerrors.ErrUnexpectedDifficulty, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type mocDifficultyManager struct {
|
||||
testDifficulty uint32
|
||||
testGenesisBits uint32
|
||||
}
|
||||
|
||||
// RequiredDifficulty returns the difficulty required for the test
|
||||
func (dm *mocDifficultyManager) RequiredDifficulty(*externalapi.DomainHash) (uint32, error) {
|
||||
return dm.testDifficulty, nil
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// BlockWindow returns a blockWindow of the given size that contains the
|
||||
// blocks in the past of startindNode, the sorting is unspecified.
|
||||
// If the number of blocks in the past of startingNode is less then windowSize,
|
||||
// blueBlockWindow returns a blockWindow of the given size that contains the
|
||||
// blues in the past of startindNode, the sorting is unspecified.
|
||||
// If the number of blues in the past of startingNode is less then windowSize,
|
||||
// the window will be padded by genesis blocks to achieve a size of windowSize.
|
||||
func (dtm *dagTraversalManager) BlockWindow(startingBlock *externalapi.DomainHash, windowSize int) ([]*externalapi.DomainHash, error) {
|
||||
func (dtm *dagTraversalManager) BlueWindow(startingBlock *externalapi.DomainHash, windowSize int) ([]*externalapi.DomainHash, error) {
|
||||
currentHash := startingBlock
|
||||
currentGHOSTDAGData, err := dtm.ghostdagDataStore.Get(dtm.databaseContext, currentHash)
|
||||
if err != nil {
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func TestBlockWindow(t *testing.T) {
|
||||
func TestBlueBlockWindow(t *testing.T) {
|
||||
tests := map[string][]*struct {
|
||||
parents []string
|
||||
id string //id is a virtual entity that is used only for tests so we can define relations between blocks without knowing their hash
|
||||
@@ -311,7 +311,7 @@ func TestBlockWindow(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
params.K = 1
|
||||
factory := consensus.NewFactory()
|
||||
tc, tearDown, err := factory.NewTestConsensus(params, false, "TestBlockWindow")
|
||||
tc, tearDown, err := factory.NewTestConsensus(params, false, "TestBlueBlockWindow")
|
||||
if err != nil {
|
||||
t.Fatalf("NewTestConsensus: %s", err)
|
||||
}
|
||||
@@ -340,9 +340,9 @@ func TestBlockWindow(t *testing.T) {
|
||||
blockByIDMap[blockData.id] = block
|
||||
idByBlockMap[*block] = blockData.id
|
||||
|
||||
window, err := tc.DAGTraversalManager().BlockWindow(block, windowSize)
|
||||
window, err := tc.DAGTraversalManager().BlueWindow(block, windowSize)
|
||||
if err != nil {
|
||||
t.Fatalf("BlockWindow: %s", err)
|
||||
t.Fatalf("BlueWindow: %s", err)
|
||||
}
|
||||
sort.Sort(testutils.NewTestGhostDAGSorter(window, tc, t))
|
||||
if err := checkWindowIDs(window, blockData.expectedWindowWithGenesisPadding, idByBlockMap); err != nil {
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package difficultymanager
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/big"
|
||||
"sort"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
"github.com/pkg/errors"
|
||||
"math"
|
||||
"math/big"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type difficultyBlock struct {
|
||||
@@ -28,13 +27,13 @@ func (dm *difficultyManager) getDifficultyBlock(blockHash *externalapi.DomainHas
|
||||
}, nil
|
||||
}
|
||||
|
||||
// blockWindow returns a blockWindow of the given size that contains the
|
||||
// blocks in the past of startindNode, the sorting is unspecified.
|
||||
// If the number of blocks in the past of startingNode is less then windowSize,
|
||||
// blueBlockWindow returns a blockWindow of the given size that contains the
|
||||
// blues in the past of startindNode, the sorting is unspecified.
|
||||
// If the number of blues in the past of startingNode is less then windowSize,
|
||||
// the window will be padded by genesis blocks to achieve a size of windowSize.
|
||||
func (dm *difficultyManager) blockWindow(startingNode *externalapi.DomainHash, windowSize int) (blockWindow, error) {
|
||||
func (dm *difficultyManager) blueBlockWindow(startingNode *externalapi.DomainHash, windowSize int) (blockWindow, error) {
|
||||
window := make(blockWindow, 0, windowSize)
|
||||
windowHashes, err := dm.dagTraversalManager.BlockWindow(startingNode, windowSize)
|
||||
windowHashes, err := dm.dagTraversalManager.BlueWindow(startingNode, windowSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package difficultymanager
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
@@ -89,7 +88,7 @@ func (dm *difficultyManager) RequiredDifficulty(blockHash *externalapi.DomainHas
|
||||
}
|
||||
|
||||
// Fetch window of dag.difficultyAdjustmentWindowSize + 1 so we can have dag.difficultyAdjustmentWindowSize block intervals
|
||||
targetsWindow, err := dm.blockWindow(bluestParent, dm.difficultyAdjustmentWindowSize+1)
|
||||
targetsWindow, err := dm.blueBlockWindow(bluestParent, dm.difficultyAdjustmentWindowSize+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ func (pmtm *pastMedianTimeManager) PastMedianTime(blockHash *externalapi.DomainH
|
||||
return header.TimeInMilliseconds(), nil
|
||||
}
|
||||
|
||||
window, err := pmtm.dagTraversalManager.BlockWindow(selectedParentHash, 2*pmtm.timestampDeviationTolerance-1)
|
||||
window, err := pmtm.dagTraversalManager.BlueWindow(selectedParentHash, 2*pmtm.timestampDeviationTolerance-1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -1,251 +0,0 @@
|
||||
package transactionvalidator_test
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/go-secp256k1"
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
|
||||
"math/big"
|
||||
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type mocPastMedianTimeManager struct {
|
||||
pastMedianTimeForTest int64
|
||||
}
|
||||
|
||||
// PastMedianTime returns the past median time for the test.
|
||||
func (mdf *mocPastMedianTimeManager) PastMedianTime(*externalapi.DomainHash) (int64, error) {
|
||||
return mdf.pastMedianTimeForTest, nil
|
||||
}
|
||||
|
||||
func TestValidateTransactionInContextAndPopulateMassAndFee(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
pastMedianManager := &mocPastMedianTimeManager{}
|
||||
factory.SetTestPastMedianTimeManager(func(int, model.DBReader, model.DAGTraversalManager, model.BlockHeaderStore,
|
||||
model.GHOSTDAGDataStore) model.PastMedianTimeManager {
|
||||
return pastMedianManager
|
||||
})
|
||||
tc, tearDown, err := factory.NewTestConsensus(params, false,
|
||||
"TestValidateTransactionInContextAndPopulateMassAndFee")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed create a NewTestConsensus: %s", err)
|
||||
}
|
||||
defer tearDown(false)
|
||||
|
||||
pastMedianManager.pastMedianTimeForTest = 1
|
||||
privateKey, err := secp256k1.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to generate a private key: %v", err)
|
||||
}
|
||||
publicKey, err := privateKey.SchnorrPublicKey()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to generate a public key: %v", err)
|
||||
}
|
||||
publicKeySerialized, err := publicKey.Serialize()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to serialize public key: %v", err)
|
||||
}
|
||||
addr, err := util.NewAddressPubKeyHashFromPublicKey(publicKeySerialized[:], params.Prefix)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to generate p2pkh address: %v", err)
|
||||
}
|
||||
scriptPublicKey, err := txscript.PayToAddrScript(addr)
|
||||
if err != nil {
|
||||
t.Fatalf("PayToAddrScript: unexpected error: %v", err)
|
||||
}
|
||||
prevOutTxID := &externalapi.DomainTransactionID{}
|
||||
prevOutPoint := externalapi.DomainOutpoint{TransactionID: *prevOutTxID, Index: 1}
|
||||
|
||||
txInput := externalapi.DomainTransactionInput{
|
||||
PreviousOutpoint: prevOutPoint,
|
||||
SignatureScript: []byte{},
|
||||
Sequence: constants.MaxTxInSequenceNum,
|
||||
UTXOEntry: utxo.NewUTXOEntry(
|
||||
100_000_000, // 1 KAS
|
||||
scriptPublicKey,
|
||||
true,
|
||||
uint64(5)),
|
||||
}
|
||||
txInputWithMaxSequence := externalapi.DomainTransactionInput{
|
||||
PreviousOutpoint: prevOutPoint,
|
||||
SignatureScript: []byte{},
|
||||
Sequence: constants.SequenceLockTimeIsSeconds,
|
||||
UTXOEntry: utxo.NewUTXOEntry(
|
||||
100000000, // 1 KAS
|
||||
scriptPublicKey,
|
||||
true,
|
||||
uint64(5)),
|
||||
}
|
||||
txInputWithLargeAmount := externalapi.DomainTransactionInput{
|
||||
PreviousOutpoint: prevOutPoint,
|
||||
SignatureScript: []byte{},
|
||||
Sequence: constants.MaxTxInSequenceNum,
|
||||
UTXOEntry: utxo.NewUTXOEntry(
|
||||
constants.MaxSompi,
|
||||
scriptPublicKey,
|
||||
true,
|
||||
uint64(5)),
|
||||
}
|
||||
|
||||
txOut := externalapi.DomainTransactionOutput{
|
||||
Value: 100000000, // 1 KAS
|
||||
ScriptPublicKey: scriptPublicKey,
|
||||
}
|
||||
txOutBigValue := externalapi.DomainTransactionOutput{
|
||||
Value: 200_000_000, // 2 KAS
|
||||
ScriptPublicKey: scriptPublicKey,
|
||||
}
|
||||
|
||||
validTx := externalapi.DomainTransaction{
|
||||
Version: constants.MaxTransactionVersion,
|
||||
Inputs: []*externalapi.DomainTransactionInput{&txInputWithMaxSequence},
|
||||
Outputs: []*externalapi.DomainTransactionOutput{&txOut},
|
||||
SubnetworkID: subnetworks.SubnetworkIDRegistry,
|
||||
Gas: 0,
|
||||
LockTime: 0}
|
||||
txWithImmatureCoinbase := externalapi.DomainTransaction{
|
||||
Version: constants.MaxTransactionVersion,
|
||||
Inputs: []*externalapi.DomainTransactionInput{&txInput},
|
||||
Outputs: []*externalapi.DomainTransactionOutput{&txOut},
|
||||
SubnetworkID: subnetworks.SubnetworkIDRegistry,
|
||||
Gas: 0,
|
||||
LockTime: 0}
|
||||
txWithLargeAmount := externalapi.DomainTransaction{
|
||||
Version: constants.MaxTransactionVersion,
|
||||
Inputs: []*externalapi.DomainTransactionInput{&txInput, &txInputWithLargeAmount},
|
||||
Outputs: []*externalapi.DomainTransactionOutput{&txOut},
|
||||
SubnetworkID: subnetworks.SubnetworkIDRegistry,
|
||||
Gas: 0,
|
||||
LockTime: 0}
|
||||
txWithBigValue := externalapi.DomainTransaction{
|
||||
Version: constants.MaxTransactionVersion,
|
||||
Inputs: []*externalapi.DomainTransactionInput{&txInput},
|
||||
Outputs: []*externalapi.DomainTransactionOutput{&txOutBigValue},
|
||||
SubnetworkID: subnetworks.SubnetworkIDRegistry,
|
||||
Gas: 0,
|
||||
LockTime: 0}
|
||||
txWithInvalidSignature := externalapi.DomainTransaction{
|
||||
Version: constants.MaxTransactionVersion,
|
||||
Inputs: []*externalapi.DomainTransactionInput{&txInput},
|
||||
Outputs: []*externalapi.DomainTransactionOutput{&txOut},
|
||||
SubnetworkID: subnetworks.SubnetworkIDRegistry,
|
||||
Gas: 0,
|
||||
LockTime: 0}
|
||||
|
||||
for i, input := range validTx.Inputs {
|
||||
signatureScript, err := txscript.SignatureScript(&validTx, i, scriptPublicKey, txscript.SigHashAll, privateKey)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a sigScript: %v", err)
|
||||
}
|
||||
input.SignatureScript = signatureScript
|
||||
}
|
||||
|
||||
povBlockHash := externalapi.NewDomainHashFromByteArray(&[32]byte{0x01})
|
||||
genesisHash := params.GenesisHash
|
||||
tc.GHOSTDAGDataStore().Stage(model.VirtualBlockHash, model.NewBlockGHOSTDAGData(
|
||||
params.BlockCoinbaseMaturity+txInput.UTXOEntry.BlockBlueScore(),
|
||||
new(big.Int),
|
||||
genesisHash,
|
||||
make([]*externalapi.DomainHash, 1000),
|
||||
make([]*externalapi.DomainHash, 1),
|
||||
nil))
|
||||
tc.GHOSTDAGDataStore().Stage(povBlockHash, model.NewBlockGHOSTDAGData(
|
||||
10,
|
||||
new(big.Int),
|
||||
genesisHash,
|
||||
make([]*externalapi.DomainHash, 1000),
|
||||
make([]*externalapi.DomainHash, 1),
|
||||
nil))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
tx *externalapi.DomainTransaction
|
||||
povBlockHash *externalapi.DomainHash
|
||||
selectedParentMedianTime int64
|
||||
isValid bool
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "Valid transaction",
|
||||
tx: &validTx,
|
||||
povBlockHash: model.VirtualBlockHash,
|
||||
selectedParentMedianTime: 1,
|
||||
isValid: true,
|
||||
expectedError: nil,
|
||||
},
|
||||
{ // The calculated block coinbase maturity is smaller than the minimum expected blockCoinbaseMaturity.
|
||||
// The povBlockHash blue score is 10 and the UTXO blue score is 5, hence the The subtraction between
|
||||
// them will yield a smaller result than the required CoinbaseMaturity (currently set to 100).
|
||||
name: "checkTransactionCoinbaseMaturity",
|
||||
tx: &txWithImmatureCoinbase,
|
||||
povBlockHash: povBlockHash,
|
||||
selectedParentMedianTime: 1,
|
||||
isValid: false,
|
||||
expectedError: ruleerrors.ErrImmatureSpend,
|
||||
},
|
||||
{ // The total inputs amount is bigger than the allowed maximum (constants.MaxSompi)
|
||||
name: "checkTransactionInputAmounts",
|
||||
tx: &txWithLargeAmount,
|
||||
povBlockHash: model.VirtualBlockHash,
|
||||
selectedParentMedianTime: 1,
|
||||
isValid: false,
|
||||
expectedError: ruleerrors.ErrBadTxOutValue,
|
||||
},
|
||||
{ // The total SompiIn (sum of inputs amount) is smaller than the total SompiOut (sum of outputs value) and hence invalid.
|
||||
name: "checkTransactionOutputAmounts",
|
||||
tx: &txWithBigValue,
|
||||
povBlockHash: model.VirtualBlockHash,
|
||||
selectedParentMedianTime: 1,
|
||||
isValid: false,
|
||||
expectedError: ruleerrors.ErrSpendTooHigh,
|
||||
},
|
||||
{ // the selectedParentMedianTime is negative and hence invalid.
|
||||
name: "checkTransactionSequenceLock",
|
||||
tx: &validTx,
|
||||
povBlockHash: model.VirtualBlockHash,
|
||||
selectedParentMedianTime: -1,
|
||||
isValid: false,
|
||||
expectedError: ruleerrors.ErrUnfinalizedTx,
|
||||
},
|
||||
{ // The SignatureScript (in the txInput) is empty and hence invalid.
|
||||
name: "checkTransactionScripts",
|
||||
tx: &txWithInvalidSignature,
|
||||
povBlockHash: model.VirtualBlockHash,
|
||||
selectedParentMedianTime: 1,
|
||||
isValid: false,
|
||||
expectedError: ruleerrors.ErrScriptValidation,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
err := tc.TransactionValidator().ValidateTransactionInContextAndPopulateMassAndFee(test.tx,
|
||||
test.povBlockHash, test.selectedParentMedianTime)
|
||||
|
||||
if test.isValid {
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error on TestValidateTransactionInContextAndPopulateMassAndFee"+
|
||||
" on test %v: %v", test.name, err)
|
||||
}
|
||||
} else {
|
||||
if err == nil || !errors.Is(err, test.expectedError) {
|
||||
t.Fatalf("TestValidateTransactionInContextAndPopulateMassAndFee: test %v:"+
|
||||
" Unexpected error: Expected to: %v, but got : %v", test.name, test.expectedError, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,326 +0,0 @@
|
||||
package miningmanager_test
|
||||
|
||||
import (
|
||||
//"bytes"
|
||||
//"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
|
||||
//"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
//"github.com/kaspanet/kaspad/domain/consensus/utils/coinbase"
|
||||
//"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
//"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
//"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
//"github.com/kaspanet/kaspad/domain/miningmanager"
|
||||
//infrastructuredatabase "github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||
//"github.com/kaspanet/kaspad/infrastructure/db/database/ldb"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
//"github.com/pkg/errors"
|
||||
//"github.com/syndtr/goleveldb/leveldb/opt"
|
||||
//"io/ioutil"
|
||||
//"os"
|
||||
//"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
//func setupDBForTest(dbName string) (infrastructuredatabase.Database, func(), error) {
|
||||
// var err error
|
||||
// tmpDir, err := ioutil.TempDir("", "setupDBManager")
|
||||
// if err != nil {
|
||||
// return nil, nil, errors.Errorf("error creating temp dir: %s", err)
|
||||
// }
|
||||
//
|
||||
// dbPath := filepath.Join(tmpDir, dbName)
|
||||
// _ = os.RemoveAll(dbPath)
|
||||
// db, err := ldb.NewLevelDB(dbPath)
|
||||
// if err != nil {
|
||||
// return nil, nil, err
|
||||
// }
|
||||
//
|
||||
// originalLDBOptions := ldb.Options
|
||||
// ldb.Options = func() *opt.Options {
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// teardown := func() {
|
||||
// db.Close()
|
||||
// ldb.Options = originalLDBOptions
|
||||
// os.RemoveAll(dbPath)
|
||||
// }
|
||||
//
|
||||
// return db, teardown, err
|
||||
//}
|
||||
|
||||
func createCoinbaseTransaction(t *testing.T, scriptPublicKey []byte, value uint64) *externalapi.DomainTransaction {
|
||||
dummyTxOut := externalapi.DomainTransactionOutput{
|
||||
Value: value,
|
||||
ScriptPublicKey: nil,
|
||||
}
|
||||
|
||||
transaction := &externalapi.DomainTransaction{
|
||||
Version: constants.MaxTransactionVersion,
|
||||
Inputs: []*externalapi.DomainTransactionInput{},
|
||||
Outputs: []*externalapi.DomainTransactionOutput{&dummyTxOut},
|
||||
LockTime: 0,
|
||||
SubnetworkID: subnetworks.SubnetworkIDCoinbase,
|
||||
}
|
||||
|
||||
return transaction
|
||||
}
|
||||
|
||||
//
|
||||
//func createTransaction(inputs []*externalapi.DomainTransactionInput, scriptPublicKey []byte, value uint64) *externalapi.DomainTransaction {
|
||||
// dummyTxOut := externalapi.DomainTransactionOutput{
|
||||
// Value: value,
|
||||
// ScriptPublicKey: scriptPublicKey,
|
||||
// }
|
||||
//
|
||||
// transaction := &externalapi.DomainTransaction{
|
||||
// Version: constants.MaxTransactionVersion,
|
||||
// Inputs: inputs,
|
||||
// Outputs: []*externalapi.DomainTransactionOutput{&dummyTxOut},
|
||||
// LockTime: 0,
|
||||
// SubnetworkID: subnetworks.SubnetworkIDNative,
|
||||
// Gas: 0,
|
||||
// }
|
||||
//
|
||||
// return transaction
|
||||
//}
|
||||
func TestMiningManager(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestBlockSize")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tc: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
tc.Database()
|
||||
|
||||
miningAddrHash := [20]byte{0x99}
|
||||
miningAddr, err := util.NewAddressPubKeyHash(miningAddrHash[:], util.Bech32PrefixKaspaTest)
|
||||
if err != nil {
|
||||
t.Fatalf("NewAddressPubKeyHash: unexpected error: %v", err)
|
||||
}
|
||||
scriptPublicKey, err := txscript.PayToAddrScript(miningAddr)
|
||||
isArchivalNode := false
|
||||
domain, err := domain.New(params, tc.Database(), isArchivalNode)
|
||||
if err != nil {
|
||||
t.Fatalf("err")
|
||||
}
|
||||
//consensusInstance, err := consensusFactory.NewConsensus(dagParams, db)
|
||||
//if err != nil {
|
||||
// t.Fatalf("NewConsensus: %v", err)
|
||||
//}
|
||||
|
||||
// Insert 10 transactions
|
||||
miningManager := domain.MiningManager()
|
||||
//miningManager := miningManagerFactory.NewMiningManager(consensusInstance, constants.MaxMassAcceptedByBlock)
|
||||
transactions := make([]*externalapi.DomainTransaction, 10)
|
||||
for i := range transactions {
|
||||
transaction := createCoinbaseTransaction(t, scriptPublicKey.Script, uint64(100000000+i))
|
||||
transactions[i] = transaction
|
||||
err = miningManager.ValidateAndInsertTransaction(transaction, true)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateAndInsertTransaction: unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Spending 10 transactions
|
||||
miningManager.HandleNewBlockTransactions(transactions)
|
||||
block, err := miningManager.GetBlockTemplate(&externalapi.DomainCoinbaseData{
|
||||
ScriptPublicKey: scriptPublicKey,
|
||||
})
|
||||
if err != nil {
|
||||
// todo
|
||||
}
|
||||
if block == nil {
|
||||
t.Fatalf("GetBlockTemplate: failed building block")
|
||||
}
|
||||
|
||||
// Check 10 transactions are not exist
|
||||
for _, tx2 := range transactions {
|
||||
for _, tx1 := range block.Transactions {
|
||||
if consensushashing.TransactionID(tx1) == consensushashing.TransactionID(tx2) {
|
||||
t.Fatalf("Spent tranasaction is still exisit")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
//
|
||||
//func TestMiningManager(t *testing.T) {
|
||||
// dagParams := &dagconfig.SimnetParams
|
||||
// consensusFactory := consensus.NewFactory()
|
||||
// miningManagerFactory := miningmanager.NewFactory()
|
||||
// db, teardownFunc, err := setupDBForTest(t.Name())
|
||||
// if err != nil {
|
||||
// t.Fatalf("Failed to setup db: %v", err)
|
||||
// }
|
||||
// defer teardownFunc()
|
||||
//
|
||||
// miningAddrHash := [20]byte{0x99}
|
||||
// miningAddr, err := util.NewAddressPubKeyHash(miningAddrHash[:], util.Bech32PrefixKaspaTest)
|
||||
// if err != nil {
|
||||
// t.Fatalf("NewAddressPubKeyHash: unexpected error: %v", err)
|
||||
// }
|
||||
// scriptPublicKey, err := txscript.PayToAddrScript(miningAddr)
|
||||
//
|
||||
// t.Run("Spending all transactions", func(t *testing.T) {
|
||||
// consensusInstance, err := consensusFactory.NewConsensus(dagParams, db)
|
||||
// if err != nil {
|
||||
// t.Fatalf("NewConsensus: %v", err)
|
||||
// }
|
||||
//
|
||||
// // Insert 10 transactions
|
||||
// miningManager := miningManagerFactory.NewMiningManager(consensusInstance, constants.MaxMassAcceptedByBlock)
|
||||
// transactions := make([]*externalapi.DomainTransaction, 10)
|
||||
// for i := range transactions {
|
||||
// transaction := createCoinbaseTransaction(t, scriptPublicKey, uint64(100000000+i))
|
||||
// transactions[i] = transaction
|
||||
// err = miningManager.ValidateAndInsertTransaction(transaction, true)
|
||||
// if err != nil {
|
||||
// t.Fatalf("ValidateAndInsertTransaction: unexpected error: %v", err)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Spending 10 transactions
|
||||
// miningManager.HandleNewBlockTransactions(transactions)
|
||||
// block := miningManager.GetBlockTemplate(&externalapi.DomainCoinbaseData{
|
||||
// ScriptPublicKey: scriptPublicKey,
|
||||
// })
|
||||
// if block == nil {
|
||||
// t.Fatalf("GetBlockTemplate: failed building block")
|
||||
// }
|
||||
//
|
||||
// // Check 10 transactions are not exist
|
||||
// for _, tx2 := range transactions {
|
||||
// for _, tx1 := range block.Transactions {
|
||||
// if consensusserialization.TransactionID(tx1) == consensusserialization.TransactionID(tx2) {
|
||||
// t.Fatalf("Spent tranasaction is still exisit")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
//
|
||||
// t.Run("Spending some transactions", func(t *testing.T) {
|
||||
// consensusInstance, err := consensusFactory.NewConsensus(dagParams, db)
|
||||
// if err != nil {
|
||||
// t.Fatalf("NewConsensus: %v", err)
|
||||
// }
|
||||
//
|
||||
// // Insert 10 transactions
|
||||
// miningManager := miningManagerFactory.NewMiningManager(consensusInstance, constants.MaxMassAcceptedByBlock)
|
||||
// transactions := make([]*externalapi.DomainTransaction, 10)
|
||||
// for i := range transactions {
|
||||
// transaction := createCoinbaseTransaction(t, scriptPublicKey, uint64(100000000+i))
|
||||
// transactions[i] = transaction
|
||||
// err = miningManager.ValidateAndInsertTransaction(transaction, true)
|
||||
// if err != nil {
|
||||
// t.Fatalf("ValidateAndInsertTransaction: unexpected error: %v", err)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Spending first 5 transactions
|
||||
// miningManager.HandleNewBlockTransactions(transactions[0:5])
|
||||
// block := miningManager.GetBlockTemplate(&externalapi.DomainCoinbaseData{
|
||||
// ScriptPublicKey: scriptPublicKey,
|
||||
// })
|
||||
// if block == nil {
|
||||
// t.Fatalf("GetBlockTemplate: failed building block")
|
||||
// }
|
||||
//
|
||||
// // Check first 5 transactions are not exist
|
||||
// for _, tx2 := range transactions[0:5] {
|
||||
// for _, tx1 := range block.Transactions {
|
||||
// if consensusserialization.TransactionID(tx1) == consensusserialization.TransactionID(tx2) {
|
||||
// t.Fatalf("Spent tranasaction is still exisit")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
//
|
||||
// t.Run("Spending transactions with unknown parents", func(t *testing.T) {
|
||||
// consensusInstance, err := consensusFactory.NewConsensus(dagParams, db)
|
||||
// if err != nil {
|
||||
// t.Fatalf("NewConsensus: %v", err)
|
||||
// }
|
||||
//
|
||||
// miningManager := miningManagerFactory.NewMiningManager(consensusInstance, constants.MaxMassAcceptedByBlock)
|
||||
// transactions := make([]*externalapi.DomainTransaction, 10)
|
||||
// parentTransactions := make([]*externalapi.DomainTransaction, len(transactions))
|
||||
// for i := range parentTransactions {
|
||||
// parentTransaction := createCoinbaseTransaction(t, scriptPublicKey, uint64(100000000+i))
|
||||
// parentTransactions[i] = parentTransaction
|
||||
// }
|
||||
//
|
||||
// // Insert transactions with unknown parents
|
||||
// for i := range transactions {
|
||||
// parentTransaction := parentTransactions[i]
|
||||
// txIn := externalapi.DomainTransactionInput{
|
||||
// PreviousOutpoint: externalapi.DomainOutpoint{TransactionID: *consensusserialization.TransactionID(parentTransaction), Index: 1},
|
||||
// SignatureScript: bytes.Repeat([]byte{0x00}, 65),
|
||||
// Sequence: appmessage.MaxTxInSequenceNum,
|
||||
// }
|
||||
// transaction := createTransaction([]*externalapi.DomainTransactionInput{&txIn}, scriptPublicKey, uint64(10+i))
|
||||
// transactions[i] = transaction
|
||||
// err = miningManager.ValidateAndInsertTransaction(transaction, true)
|
||||
// if err != nil {
|
||||
// t.Fatalf("ValidateAndInsertTransaction: unexpected error: %v", err)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Check transactions with unknown parents
|
||||
// block := miningManager.GetBlockTemplate(&externalapi.DomainCoinbaseData{
|
||||
// ScriptPublicKey: scriptPublicKey,
|
||||
// })
|
||||
// if block == nil {
|
||||
// t.Fatalf("GetBlockTemplate: failed building block")
|
||||
// }
|
||||
//
|
||||
// for _, tx1 := range transactions {
|
||||
// for _, tx2 := range block.Transactions {
|
||||
// if consensusserialization.TransactionID(tx1) == consensusserialization.TransactionID(tx2) {
|
||||
// t.Fatalf("Tranasaction with unknown parents is exisit")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Add the missing parents
|
||||
// for _, parentTransaction := range parentTransactions {
|
||||
// err = miningManager.ValidateAndInsertTransaction(parentTransaction, true)
|
||||
// if err != nil {
|
||||
// t.Fatalf("ValidateAndInsertTransaction: unexpected error: %v", err)
|
||||
// }
|
||||
// }
|
||||
// block = miningManager.GetBlockTemplate(&externalapi.DomainCoinbaseData{
|
||||
// ScriptPublicKey: scriptPublicKey,
|
||||
// })
|
||||
// if block == nil {
|
||||
// t.Fatalf("GetBlockTemplate: failed building block")
|
||||
// }
|
||||
//
|
||||
// numberOfFoundTransactions := 0
|
||||
// for _, tx1 := range transactions {
|
||||
// for _, tx2 := range block.Transactions {
|
||||
// if consensusserialization.TransactionID(tx1) == consensusserialization.TransactionID(tx2) {
|
||||
// numberOfFoundTransactions++
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if len(transactions) != numberOfFoundTransactions{
|
||||
// t.Fatalf("Not all transactions are exist, expected %v, but got %v", len(transactions), numberOfFoundTransactions)
|
||||
// }
|
||||
// })
|
||||
//}
|
||||
2
go.mod
2
go.mod
@@ -1,6 +1,6 @@
|
||||
module github.com/kaspanet/kaspad
|
||||
|
||||
go 1.16
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd
|
||||
|
||||
@@ -10,8 +10,8 @@ const validCharacters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrs
|
||||
|
||||
const (
|
||||
appMajor uint = 0
|
||||
appMinor uint = 9
|
||||
appPatch uint = 0
|
||||
appMinor uint = 8
|
||||
appPatch uint = 9
|
||||
)
|
||||
|
||||
// appBuild is defined as a variable so it can be overridden during the build
|
||||
|
||||
Reference in New Issue
Block a user