mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-11-23 22:15:54 +00:00
commit for update the branch with current version
This commit is contained in:
commit
5d03d72bd3
49
.github/workflows/go-race.yml
vendored
Normal file
49
.github/workflows/go-race.yml
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
name: Go-Race
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 0 * * *"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
race_test:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
branch: [ master, latest ]
|
||||||
|
name: Race detection on ${{ matrix.branch }}
|
||||||
|
steps:
|
||||||
|
- name: Check out code into the Go module directory
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up Go 1.x
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.15
|
||||||
|
|
||||||
|
- name: Set scheduled branch name
|
||||||
|
shell: bash
|
||||||
|
if: github.event_name == 'schedule'
|
||||||
|
run: |
|
||||||
|
if [ "${{ matrix.branch }}" == "master" ]; then
|
||||||
|
echo "run_on=master" >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
|
if [ "${{ matrix.branch }}" == "latest" ]; then
|
||||||
|
branch=$(git branch -r | grep 'v\([0-9]\+\.\)\([0-9]\+\.\)\([0-9]\+\)-dev' | sort -Vr | head -1 | xargs)
|
||||||
|
echo "run_on=${branch}" >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Set manual branch name
|
||||||
|
shell: bash
|
||||||
|
if: github.event_name == 'workflow_dispatch'
|
||||||
|
run: echo "run_on=${{ github.ref }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Test with race detector
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
git checkout "${{ env.run_on }}"
|
||||||
|
git status
|
||||||
|
go test -race ./...
|
||||||
9
.github/workflows/go.yml
vendored
9
.github/workflows/go.yml
vendored
@ -11,6 +11,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ ubuntu-16.04, macos-10.15 ]
|
os: [ ubuntu-16.04, macos-10.15 ]
|
||||||
name: Testing on on ${{ matrix.os }}
|
name: Testing on on ${{ matrix.os }}
|
||||||
@ -34,7 +35,7 @@ jobs:
|
|||||||
- name: Set up Go 1.x
|
- name: Set up Go 1.x
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.15
|
go-version: 1.16
|
||||||
|
|
||||||
|
|
||||||
# Source: https://github.com/actions/cache/blob/main/examples.md#go---modules
|
# Source: https://github.com/actions/cache/blob/main/examples.md#go---modules
|
||||||
@ -48,7 +49,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
shell: bash
|
shell: bash
|
||||||
run: ./build_and_test.sh
|
run: ./build_and_test.sh -v
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
@ -60,10 +61,10 @@ jobs:
|
|||||||
- name: Set up Go 1.x
|
- name: Set up Go 1.x
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.15
|
go-version: 1.16
|
||||||
|
|
||||||
- name: Create coverage file
|
- name: Create coverage file
|
||||||
run: go test -covermode=atomic -coverpkg=./... -coverprofile coverage.txt ./...
|
run: go test -v -covermode=atomic -coverpkg=./... -coverprofile coverage.txt ./...
|
||||||
|
|
||||||
- name: Upload coverage file
|
- name: Upload coverage file
|
||||||
run: bash <(curl -s https://codecov.io/bash)
|
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
|
## Requirements
|
||||||
|
|
||||||
Go 1.15 or later.
|
Go 1.16 or later.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package flowcontext
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||||
|
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
@ -98,6 +99,10 @@ func (f *FlowContext) SharedRequestedBlocks() *blockrelay.SharedRequestedBlocks
|
|||||||
|
|
||||||
// AddBlock adds the given block to the DAG and propagates it.
|
// AddBlock adds the given block to the DAG and propagates it.
|
||||||
func (f *FlowContext) AddBlock(block *externalapi.DomainBlock) error {
|
func (f *FlowContext) AddBlock(block *externalapi.DomainBlock) error {
|
||||||
|
if len(block.Transactions) == 0 {
|
||||||
|
return protocolerrors.Errorf(false, "cannot add header only block")
|
||||||
|
}
|
||||||
|
|
||||||
blockInsertionResult, err := f.Domain().Consensus().ValidateAndInsertBlock(block)
|
blockInsertionResult, err := f.Domain().Consensus().ValidateAndInsertBlock(block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.As(err, &ruleerrors.RuleError{}) {
|
if errors.As(err, &ruleerrors.RuleError{}) {
|
||||||
|
|||||||
@ -104,6 +104,11 @@ func (flow *handleRelayInvsFlow) start() error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = flow.banIfBlockIsHeaderOnly(block)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
log.Debugf("Processing block %s", inv.Hash)
|
log.Debugf("Processing block %s", inv.Hash)
|
||||||
missingParents, blockInsertionResult, err := flow.processBlock(block)
|
missingParents, blockInsertionResult, err := flow.processBlock(block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -140,6 +145,15 @@ func (flow *handleRelayInvsFlow) start() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (flow *handleRelayInvsFlow) banIfBlockIsHeaderOnly(block *externalapi.DomainBlock) error {
|
||||||
|
if len(block.Transactions) == 0 {
|
||||||
|
return protocolerrors.Errorf(true, "sent header of %s block where expected block with body",
|
||||||
|
consensushashing.BlockHash(block))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (flow *handleRelayInvsFlow) readInv() (*appmessage.MsgInvRelayBlock, error) {
|
func (flow *handleRelayInvsFlow) readInv() (*appmessage.MsgInvRelayBlock, error) {
|
||||||
if len(flow.invsQueue) > 0 {
|
if len(flow.invsQueue) > 0 {
|
||||||
var inv *appmessage.MsgInvRelayBlock
|
var inv *appmessage.MsgInvRelayBlock
|
||||||
|
|||||||
@ -533,6 +533,11 @@ func (flow *handleRelayInvsFlow) syncMissingBlockBodies(highHash *externalapi.Do
|
|||||||
return protocolerrors.Errorf(true, "expected block %s but got %s", expectedHash, blockHash)
|
return protocolerrors.Errorf(true, "expected block %s but got %s", expectedHash, blockHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = flow.banIfBlockIsHeaderOnly(block)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
blockInsertionResult, err := flow.Domain().Consensus().ValidateAndInsertBlock(block)
|
blockInsertionResult, err := flow.Domain().Consensus().ValidateAndInsertBlock(block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, ruleerrors.ErrDuplicateBlock) {
|
if errors.Is(err, ruleerrors.ErrDuplicateBlock) {
|
||||||
|
|||||||
@ -24,6 +24,19 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"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{
|
var orphanBlock = &externalapi.DomainBlock{
|
||||||
Header: blockheader.NewImmutableBlockHeader(
|
Header: blockheader.NewImmutableBlockHeader(
|
||||||
constants.MaxBlockVersion,
|
constants.MaxBlockVersion,
|
||||||
@ -35,6 +48,7 @@ var orphanBlock = &externalapi.DomainBlock{
|
|||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
|
Transactions: []*externalapi.DomainTransaction{{}},
|
||||||
}
|
}
|
||||||
|
|
||||||
var validPruningPointBlock = &externalapi.DomainBlock{
|
var validPruningPointBlock = &externalapi.DomainBlock{
|
||||||
@ -48,6 +62,7 @@ var validPruningPointBlock = &externalapi.DomainBlock{
|
|||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
|
Transactions: []*externalapi.DomainTransaction{{}},
|
||||||
}
|
}
|
||||||
|
|
||||||
var invalidPruningPointBlock = &externalapi.DomainBlock{
|
var invalidPruningPointBlock = &externalapi.DomainBlock{
|
||||||
@ -61,6 +76,7 @@ var invalidPruningPointBlock = &externalapi.DomainBlock{
|
|||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
|
Transactions: []*externalapi.DomainTransaction{{}},
|
||||||
}
|
}
|
||||||
|
|
||||||
var unexpectedIBDBlock = &externalapi.DomainBlock{
|
var unexpectedIBDBlock = &externalapi.DomainBlock{
|
||||||
@ -74,6 +90,7 @@ var unexpectedIBDBlock = &externalapi.DomainBlock{
|
|||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
|
Transactions: []*externalapi.DomainTransaction{{}},
|
||||||
}
|
}
|
||||||
|
|
||||||
var invalidBlock = &externalapi.DomainBlock{
|
var invalidBlock = &externalapi.DomainBlock{
|
||||||
@ -87,6 +104,7 @@ var invalidBlock = &externalapi.DomainBlock{
|
|||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
|
Transactions: []*externalapi.DomainTransaction{{}},
|
||||||
}
|
}
|
||||||
|
|
||||||
var unknownBlockHash = externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})
|
var unknownBlockHash = externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})
|
||||||
@ -95,6 +113,7 @@ var validPruningPointHash = consensushashing.BlockHash(validPruningPointBlock)
|
|||||||
var invalidBlockHash = consensushashing.BlockHash(invalidBlock)
|
var invalidBlockHash = consensushashing.BlockHash(invalidBlock)
|
||||||
var invalidPruningPointHash = consensushashing.BlockHash(invalidPruningPointBlock)
|
var invalidPruningPointHash = consensushashing.BlockHash(invalidPruningPointBlock)
|
||||||
var orphanBlockHash = consensushashing.BlockHash(orphanBlock)
|
var orphanBlockHash = consensushashing.BlockHash(orphanBlock)
|
||||||
|
var headerOnlyBlockHash = consensushashing.BlockHash(headerOnlyBlock)
|
||||||
|
|
||||||
type fakeRelayInvsContext struct {
|
type fakeRelayInvsContext struct {
|
||||||
testName string
|
testName string
|
||||||
@ -450,6 +469,29 @@ func TestHandleRelayInvs(t *testing.T) {
|
|||||||
expectsBan: true,
|
expectsBan: true,
|
||||||
expectsErrToContain: "got unrequested block",
|
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",
|
name: "sending invalid block",
|
||||||
funcToExecute: func(t *testing.T, incomingRoute, outgoingRoute *router.Route, context *fakeRelayInvsContext) {
|
funcToExecute: func(t *testing.T, incomingRoute, outgoingRoute *router.Route, context *fakeRelayInvsContext) {
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
package rpchandlers_test
|
package rpchandlers_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/app/appmessage"
|
"github.com/kaspanet/kaspad/app/appmessage"
|
||||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||||
"github.com/kaspanet/kaspad/app/rpc/rpchandlers"
|
"github.com/kaspanet/kaspad/app/rpc/rpchandlers"
|
||||||
@ -12,9 +16,6 @@ import (
|
|||||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||||
"github.com/kaspanet/kaspad/domain/miningmanager"
|
"github.com/kaspanet/kaspad/domain/miningmanager"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"testing"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type fakeDomain struct {
|
type fakeDomain struct {
|
||||||
@ -65,66 +66,79 @@ func TestHandleGetBlocks(t *testing.T) {
|
|||||||
return antipast
|
return antipast
|
||||||
}
|
}
|
||||||
|
|
||||||
upBfsOrder := make([]*externalapi.DomainHash, 0, 30)
|
// Create a DAG with the following structure:
|
||||||
selectedParent := params.GenesisHash
|
// merging block
|
||||||
upBfsOrder = append(upBfsOrder, selectedParent)
|
// / | \
|
||||||
|
// split1 split2 split3
|
||||||
|
// \ | /
|
||||||
|
// merging block
|
||||||
|
// / | \
|
||||||
|
// split1 split2 split3
|
||||||
|
// \ | /
|
||||||
|
// etc.
|
||||||
|
expectedOrder := make([]*externalapi.DomainHash, 0, 40)
|
||||||
|
mergingBlock := params.GenesisHash
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
parents := make([]*externalapi.DomainHash, 0, 3)
|
splitBlocks := make([]*externalapi.DomainHash, 0, 3)
|
||||||
for j := 0; j < 4; j++ {
|
for j := 0; j < 3; j++ {
|
||||||
blockHash, _, err := tc.AddBlock([]*externalapi.DomainHash{selectedParent}, nil, nil)
|
blockHash, _, err := tc.AddBlock([]*externalapi.DomainHash{mergingBlock}, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed adding block: %v", err)
|
t.Fatalf("Failed adding block: %v", err)
|
||||||
}
|
}
|
||||||
parents = append(parents, blockHash)
|
splitBlocks = append(splitBlocks, blockHash)
|
||||||
upBfsOrder = append(upBfsOrder, blockHash)
|
|
||||||
}
|
}
|
||||||
selectedParent, _, err = tc.AddBlock(parents, nil, nil)
|
sort.Sort(sort.Reverse(testutils.NewTestGhostDAGSorter(splitBlocks, tc, t)))
|
||||||
|
restOfSplitBlocks, selectedParent := splitBlocks[:len(splitBlocks)-1], splitBlocks[len(splitBlocks)-1]
|
||||||
|
expectedOrder = append(expectedOrder, selectedParent)
|
||||||
|
expectedOrder = append(expectedOrder, restOfSplitBlocks...)
|
||||||
|
|
||||||
|
mergingBlock, _, err = tc.AddBlock(splitBlocks, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed adding block: %v", err)
|
t.Fatalf("Failed adding block: %v", err)
|
||||||
}
|
}
|
||||||
upBfsOrder = append(upBfsOrder, selectedParent)
|
expectedOrder = append(expectedOrder, mergingBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
virtualSelectedParent, err := tc.GetVirtualSelectedParent()
|
virtualSelectedParent, err := tc.GetVirtualSelectedParent()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed getting SelectedParent: %v", err)
|
t.Fatalf("Failed getting SelectedParent: %v", err)
|
||||||
}
|
}
|
||||||
if !virtualSelectedParent.Equal(upBfsOrder[len(upBfsOrder)-1]) {
|
if !virtualSelectedParent.Equal(expectedOrder[len(expectedOrder)-1]) {
|
||||||
t.Fatalf("Expected %s to be selectedParent, instead found: %s", upBfsOrder[len(upBfsOrder)-1], virtualSelectedParent)
|
t.Fatalf("Expected %s to be selectedParent, instead found: %s", expectedOrder[len(expectedOrder)-1], virtualSelectedParent)
|
||||||
}
|
}
|
||||||
|
|
||||||
requestSelectedParent := getBlocks(virtualSelectedParent)
|
requestSelectedParent := getBlocks(virtualSelectedParent)
|
||||||
if !reflect.DeepEqual(requestSelectedParent.BlockHashes, hashes.ToStrings([]*externalapi.DomainHash{virtualSelectedParent})) {
|
if !reflect.DeepEqual(requestSelectedParent.BlockHashes, hashes.ToStrings([]*externalapi.DomainHash{virtualSelectedParent})) {
|
||||||
t.Fatalf("TestSyncManager_GetHashesBetween expected %v\n == \n%v", requestSelectedParent.BlockHashes, virtualSelectedParent)
|
t.Fatalf("TestHandleGetBlocks expected:\n%v\nactual:\n%v", virtualSelectedParent, requestSelectedParent.BlockHashes)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, blockHash := range upBfsOrder {
|
for i, blockHash := range expectedOrder {
|
||||||
expectedBlocks := filterAntiPast(blockHash, upBfsOrder)
|
expectedBlocks := filterAntiPast(blockHash, expectedOrder)
|
||||||
// sort the slice in the order GetBlocks is returning them
|
|
||||||
sort.Sort(sort.Reverse(testutils.NewTestGhostDAGSorter(expectedBlocks, tc, t)))
|
|
||||||
expectedBlocks = append([]*externalapi.DomainHash{blockHash}, expectedBlocks...)
|
expectedBlocks = append([]*externalapi.DomainHash{blockHash}, expectedBlocks...)
|
||||||
|
|
||||||
blocks := getBlocks(blockHash)
|
actualBlocks := getBlocks(blockHash)
|
||||||
if !reflect.DeepEqual(blocks.BlockHashes, hashes.ToStrings(expectedBlocks)) {
|
if !reflect.DeepEqual(actualBlocks.BlockHashes, hashes.ToStrings(expectedBlocks)) {
|
||||||
t.Fatalf("TestSyncManager_GetHashesBetween %d expected %s\n == \n%s", i, blocks.BlockHashes, hashes.ToStrings(expectedBlocks))
|
t.Fatalf("TestHandleGetBlocks %d \nexpected: \n%v\nactual:\n%v", i,
|
||||||
|
hashes.ToStrings(expectedBlocks), actualBlocks.BlockHashes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make explitly sure that if lowHash==highHash we get a slice with a single hash.
|
// Make explicitly sure that if lowHash==highHash we get a slice with a single hash.
|
||||||
blocks := getBlocks(virtualSelectedParent)
|
actualBlocks := getBlocks(virtualSelectedParent)
|
||||||
if !reflect.DeepEqual(blocks.BlockHashes, []string{virtualSelectedParent.String()}) {
|
if !reflect.DeepEqual(actualBlocks.BlockHashes, []string{virtualSelectedParent.String()}) {
|
||||||
t.Fatalf("TestSyncManager_GetHashesBetween expected blocks to contain just '%s', instead got: \n%s", virtualSelectedParent, blocks.BlockHashes)
|
t.Fatalf("TestHandleGetBlocks expected blocks to contain just '%s', instead got: \n%v",
|
||||||
|
virtualSelectedParent, actualBlocks.BlockHashes)
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Sort(sort.Reverse(testutils.NewTestGhostDAGSorter(upBfsOrder, tc, t)))
|
expectedOrder = append([]*externalapi.DomainHash{params.GenesisHash}, expectedOrder...)
|
||||||
requestAllViaNil := getBlocks(nil)
|
actualOrder := getBlocks(nil)
|
||||||
if !reflect.DeepEqual(requestAllViaNil.BlockHashes, hashes.ToStrings(upBfsOrder)) {
|
if !reflect.DeepEqual(actualOrder.BlockHashes, hashes.ToStrings(expectedOrder)) {
|
||||||
t.Fatalf("TestSyncManager_GetHashesBetween expected %v\n == \n%v", requestAllViaNil.BlockHashes, upBfsOrder)
|
t.Fatalf("TestHandleGetBlocks \nexpected: %v \nactual:\n%v", expectedOrder, actualOrder.BlockHashes)
|
||||||
}
|
}
|
||||||
|
|
||||||
requestAllExplictly := getBlocks(params.GenesisHash)
|
requestAllExplictly := getBlocks(params.GenesisHash)
|
||||||
if !reflect.DeepEqual(requestAllExplictly.BlockHashes, hashes.ToStrings(upBfsOrder)) {
|
if !reflect.DeepEqual(requestAllExplictly.BlockHashes, hashes.ToStrings(expectedOrder)) {
|
||||||
t.Fatalf("TestSyncManager_GetHashesBetween expected %v\n == \n%v", requestAllExplictly.BlockHashes, upBfsOrder)
|
t.Fatalf("TestHandleGetBlocks \nexpected: \n%v\n. actual:\n%v", expectedOrder, requestAllExplictly.BlockHashes)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package rpchandlers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kaspanet/kaspad/app/appmessage"
|
"github.com/kaspanet/kaspad/app/appmessage"
|
||||||
|
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||||
@ -25,9 +26,10 @@ func HandleSubmitBlock(context *rpccontext.Context, _ *router.Router, request ap
|
|||||||
|
|
||||||
err := context.ProtocolManager.AddBlock(domainBlock)
|
err := context.ProtocolManager.AddBlock(domainBlock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.As(err, &ruleerrors.RuleError{}) {
|
if !errors.As(err, &ruleerrors.RuleError{}) || !errors.As(err, &protocolerrors.ProtocolError{}) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &appmessage.SubmitBlockResponseMessage{
|
return &appmessage.SubmitBlockResponseMessage{
|
||||||
Error: appmessage.RPCErrorf("Block rejected. Reason: %s", err),
|
Error: appmessage.RPCErrorf("Block rejected. Reason: %s", err),
|
||||||
RejectReason: appmessage.RejectReasonBlockInvalid,
|
RejectReason: appmessage.RejectReasonBlockInvalid,
|
||||||
|
|||||||
@ -4,7 +4,7 @@ kaspactl is an RPC client for kaspad
|
|||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
Go 1.15 or later.
|
Go 1.16 or later.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
# -- multistage docker build: stage #1: build stage
|
# -- multistage docker build: stage #1: build stage
|
||||||
FROM golang:1.15-alpine AS build
|
FROM golang:1.16-alpine AS build
|
||||||
|
|
||||||
RUN mkdir -p /go/src/github.com/kaspanet/kaspad
|
RUN mkdir -p /go/src/github.com/kaspanet/kaspad
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ Kaspaminer is a CPU-based miner for kaspad
|
|||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
Go 1.15 or later.
|
Go 1.16 or later.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
# -- multistage docker build: stage #1: build stage
|
# -- multistage docker build: stage #1: build stage
|
||||||
FROM golang:1.15-alpine AS build
|
FROM golang:1.16-alpine AS build
|
||||||
|
|
||||||
RUN mkdir -p /go/src/github.com/kaspanet/kaspad
|
RUN mkdir -p /go/src/github.com/kaspanet/kaspad
|
||||||
|
|
||||||
|
|||||||
@ -2,13 +2,14 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
nativeerrors "errors"
|
nativeerrors "errors"
|
||||||
"github.com/kaspanet/kaspad/cmd/kaspaminer/templatemanager"
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/pow"
|
|
||||||
"github.com/kaspanet/kaspad/util/difficulty"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/cmd/kaspaminer/templatemanager"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/model/pow"
|
||||||
|
"github.com/kaspanet/kaspad/util/difficulty"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
@ -155,11 +156,15 @@ func mineNextBlock(mineWhenNotSynced bool) *externalapi.DomainBlock {
|
|||||||
|
|
||||||
func getBlockForMining(mineWhenNotSynced bool) *externalapi.DomainBlock {
|
func getBlockForMining(mineWhenNotSynced bool) *externalapi.DomainBlock {
|
||||||
tryCount := 0
|
tryCount := 0
|
||||||
|
|
||||||
|
const sleepTime = 500 * time.Millisecond
|
||||||
|
const sleepTimeWhenNotSynced = 5 * time.Second
|
||||||
|
|
||||||
for {
|
for {
|
||||||
tryCount++
|
tryCount++
|
||||||
const sleepTime = 500 * time.Millisecond
|
|
||||||
shouldLog := (tryCount-1)%10 == 0
|
shouldLog := (tryCount-1)%10 == 0
|
||||||
template := templatemanager.Get()
|
template, isSynced := templatemanager.Get()
|
||||||
if template == nil {
|
if template == nil {
|
||||||
if shouldLog {
|
if shouldLog {
|
||||||
log.Info("Waiting for the initial template")
|
log.Info("Waiting for the initial template")
|
||||||
@ -167,15 +172,15 @@ func getBlockForMining(mineWhenNotSynced bool) *externalapi.DomainBlock {
|
|||||||
time.Sleep(sleepTime)
|
time.Sleep(sleepTime)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !template.IsSynced && !mineWhenNotSynced {
|
if !isSynced && !mineWhenNotSynced {
|
||||||
if shouldLog {
|
if shouldLog {
|
||||||
log.Warnf("Kaspad is not synced. Skipping current block template")
|
log.Warnf("Kaspad is not synced. Skipping current block template")
|
||||||
}
|
}
|
||||||
time.Sleep(sleepTime)
|
time.Sleep(sleepTimeWhenNotSynced)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
return appmessage.MsgBlockToDomainBlock(template.MsgBlock)
|
return template
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,22 +2,31 @@ package templatemanager
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kaspanet/kaspad/app/appmessage"
|
"github.com/kaspanet/kaspad/app/appmessage"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
var currentTemplate *appmessage.GetBlockTemplateResponseMessage
|
var currentTemplate *externalapi.DomainBlock
|
||||||
|
var isSynced bool
|
||||||
var lock = &sync.Mutex{}
|
var lock = &sync.Mutex{}
|
||||||
|
|
||||||
// Get returns the template to work on
|
// Get returns the template to work on
|
||||||
func Get() *appmessage.GetBlockTemplateResponseMessage {
|
func Get() (*externalapi.DomainBlock, bool) {
|
||||||
lock.Lock()
|
lock.Lock()
|
||||||
defer lock.Unlock()
|
defer lock.Unlock()
|
||||||
return currentTemplate
|
// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set sets the current template to work on
|
// Set sets the current template to work on
|
||||||
func Set(template *appmessage.GetBlockTemplateResponseMessage) {
|
func Set(template *appmessage.GetBlockTemplateResponseMessage) {
|
||||||
|
block := appmessage.MsgBlockToDomainBlock(template.MsgBlock)
|
||||||
lock.Lock()
|
lock.Lock()
|
||||||
defer lock.Unlock()
|
defer lock.Unlock()
|
||||||
currentTemplate = template
|
currentTemplate = block
|
||||||
|
isSynced = template.IsSynced
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ It is capable of generating wallet key-pairs, printing a wallet's current balanc
|
|||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
Go 1.15 or later.
|
Go 1.16 or later.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
# -- multistage docker build: stage #1: build stage
|
# -- multistage docker build: stage #1: build stage
|
||||||
FROM golang:1.15-alpine AS build
|
FROM golang:1.16-alpine AS build
|
||||||
|
|
||||||
RUN mkdir -p /go/src/github.com/kaspanet/kaspad
|
RUN mkdir -p /go/src/github.com/kaspanet/kaspad
|
||||||
|
|
||||||
@ -16,9 +16,12 @@ RUN go get -u golang.org/x/lint/golint \
|
|||||||
COPY go.mod .
|
COPY go.mod .
|
||||||
COPY go.sum .
|
COPY go.sum .
|
||||||
|
|
||||||
|
# Cache kaspad dependencies
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN NO_PARALLEL=1 ./build_and_test.sh
|
RUN ./build_and_test.sh
|
||||||
|
|
||||||
# --- multistage docker build: stage #2: runtime image
|
# --- multistage docker build: stage #2: runtime image
|
||||||
FROM alpine
|
FROM alpine
|
||||||
|
|||||||
@ -1,6 +1,21 @@
|
|||||||
package consensus
|
package consensus
|
||||||
|
|
||||||
import "github.com/kaspanet/kaspad/domain/consensus/model"
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
|
"math/big"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
// GHOSTDAGManagerConstructor is the function signature for a constructor of a type implementing model.GHOSTDAGManager
|
// 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
|
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
|
||||||
|
|||||||
@ -63,19 +63,25 @@ type Factory interface {
|
|||||||
SetTestGHOSTDAGManager(ghostdagConstructor GHOSTDAGManagerConstructor)
|
SetTestGHOSTDAGManager(ghostdagConstructor GHOSTDAGManagerConstructor)
|
||||||
SetTestLevelDBCacheSize(cacheSizeMiB int)
|
SetTestLevelDBCacheSize(cacheSizeMiB int)
|
||||||
SetTestPreAllocateCache(preallocateCaches bool)
|
SetTestPreAllocateCache(preallocateCaches bool)
|
||||||
|
SetTestPastMedianTimeManager(medianTimeConstructor PastMedianTimeManagerConstructor)
|
||||||
|
SetTestDifficultyManager(difficultyConstructor DifficultyManagerConstructor)
|
||||||
}
|
}
|
||||||
|
|
||||||
type factory struct {
|
type factory struct {
|
||||||
dataDir string
|
dataDir string
|
||||||
ghostdagConstructor GHOSTDAGManagerConstructor
|
ghostdagConstructor GHOSTDAGManagerConstructor
|
||||||
cacheSizeMiB *int
|
pastMedianTimeConsructor PastMedianTimeManagerConstructor
|
||||||
preallocateCaches *bool
|
difficultyConstructor DifficultyManagerConstructor
|
||||||
|
cacheSizeMiB *int
|
||||||
|
preallocateCaches *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFactory creates a new Consensus factory
|
// NewFactory creates a new Consensus factory
|
||||||
func NewFactory() Factory {
|
func NewFactory() Factory {
|
||||||
return &factory{
|
return &factory{
|
||||||
ghostdagConstructor: ghostdagmanager.New,
|
ghostdagConstructor: ghostdagmanager.New,
|
||||||
|
pastMedianTimeConsructor: pastmediantimemanager.New,
|
||||||
|
difficultyConstructor: difficultymanager.New,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +150,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
|||||||
reachabilityDataStore,
|
reachabilityDataStore,
|
||||||
ghostdagManager,
|
ghostdagManager,
|
||||||
consensusStateStore)
|
consensusStateStore)
|
||||||
pastMedianTimeManager := pastmediantimemanager.New(
|
pastMedianTimeManager := f.pastMedianTimeConsructor(
|
||||||
dagParams.TimestampDeviationTolerance,
|
dagParams.TimestampDeviationTolerance,
|
||||||
dbManager,
|
dbManager,
|
||||||
dagTraversalManager,
|
dagTraversalManager,
|
||||||
@ -159,7 +165,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
|||||||
dbManager,
|
dbManager,
|
||||||
pastMedianTimeManager,
|
pastMedianTimeManager,
|
||||||
ghostdagDataStore)
|
ghostdagDataStore)
|
||||||
difficultyManager := difficultymanager.New(
|
difficultyManager := f.difficultyConstructor(
|
||||||
dbManager,
|
dbManager,
|
||||||
ghostdagManager,
|
ghostdagManager,
|
||||||
ghostdagDataStore,
|
ghostdagDataStore,
|
||||||
@ -466,6 +472,15 @@ func (f *factory) SetTestGHOSTDAGManager(ghostdagConstructor GHOSTDAGManagerCons
|
|||||||
f.ghostdagConstructor = ghostdagConstructor
|
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) {
|
func (f *factory) SetTestLevelDBCacheSize(cacheSizeMiB int) {
|
||||||
f.cacheSizeMiB = &cacheSizeMiB
|
f.cacheSizeMiB = &cacheSizeMiB
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,8 @@ package consensus
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||||
|
"github.com/kaspanet/kaspad/util/panics"
|
||||||
)
|
)
|
||||||
|
|
||||||
var log, _ = logger.Get(logger.SubsystemTags.BDAG)
|
var log, _ = logger.Get(logger.SubsystemTags.BDAG)
|
||||||
|
var spawn = panics.GoroutineWrapperFunc(log)
|
||||||
|
|||||||
@ -11,7 +11,7 @@ type DAGTraversalManager interface {
|
|||||||
// from lowHash (exclusive) to highHash (inclusive) over highHash's selected parent chain
|
// from lowHash (exclusive) to highHash (inclusive) over highHash's selected parent chain
|
||||||
SelectedChildIterator(highHash, lowHash *externalapi.DomainHash) (BlockIterator, error)
|
SelectedChildIterator(highHash, lowHash *externalapi.DomainHash) (BlockIterator, error)
|
||||||
Anticone(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error)
|
Anticone(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error)
|
||||||
BlueWindow(highHash *externalapi.DomainHash, windowSize int) ([]*externalapi.DomainHash, error)
|
BlockWindow(highHash *externalapi.DomainHash, windowSize int) ([]*externalapi.DomainHash, error)
|
||||||
NewDownHeap() BlockHeap
|
NewDownHeap() BlockHeap
|
||||||
NewUpHeap() BlockHeap
|
NewUpHeap() BlockHeap
|
||||||
CalculateChainPath(
|
CalculateChainPath(
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
package testapi
|
package testapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/db/database"
|
"github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||||
"io"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// MineJSONBlockType indicates which type of blocks MineJSON mines
|
// MineJSONBlockType indicates which type of blocks MineJSON mines
|
||||||
@ -50,6 +51,8 @@ type TestConsensus interface {
|
|||||||
MineJSON(r io.Reader, blockType MineJSONBlockType) (tips []*externalapi.DomainHash, err error)
|
MineJSON(r io.Reader, blockType MineJSONBlockType) (tips []*externalapi.DomainHash, err error)
|
||||||
DiscardAllStores()
|
DiscardAllStores()
|
||||||
|
|
||||||
|
RenderDAGToDot(filename string) error
|
||||||
|
|
||||||
AcceptanceDataStore() model.AcceptanceDataStore
|
AcceptanceDataStore() model.AcceptanceDataStore
|
||||||
BlockHeaderStore() model.BlockHeaderStore
|
BlockHeaderStore() model.BlockHeaderStore
|
||||||
BlockRelationStore() model.BlockRelationStore
|
BlockRelationStore() model.BlockRelationStore
|
||||||
|
|||||||
@ -1,66 +0,0 @@
|
|||||||
// Copyright (c) 2015-2017 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package blocklogger
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
||||||
"github.com/kaspanet/kaspad/util/mstime"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
receivedLogBlocks int64
|
|
||||||
receivedLogHeaders int64
|
|
||||||
receivedLogTransactions int64
|
|
||||||
lastBlockLogTime time.Time
|
|
||||||
)
|
|
||||||
|
|
||||||
// LogBlock logs a new block blue score as an information message
|
|
||||||
// to show progress to the user. In order to prevent spam, it limits logging to
|
|
||||||
// one message every 10 seconds with duration and totals included.
|
|
||||||
func LogBlock(block *externalapi.DomainBlock) {
|
|
||||||
if len(block.Transactions) == 0 {
|
|
||||||
receivedLogHeaders++
|
|
||||||
} else {
|
|
||||||
receivedLogBlocks++
|
|
||||||
}
|
|
||||||
|
|
||||||
receivedLogTransactions += int64(len(block.Transactions))
|
|
||||||
|
|
||||||
now := time.Now()
|
|
||||||
duration := now.Sub(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 receivedLogBlocks == 1 {
|
|
||||||
blockStr = "block"
|
|
||||||
}
|
|
||||||
|
|
||||||
txStr := "transactions"
|
|
||||||
if receivedLogTransactions == 1 {
|
|
||||||
txStr = "transaction"
|
|
||||||
}
|
|
||||||
|
|
||||||
headerStr := "headers"
|
|
||||||
if receivedLogBlocks == 1 {
|
|
||||||
headerStr = "header"
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Infof("Processed %d %s and %d %s in the last %s (%d %s, %s)",
|
|
||||||
receivedLogBlocks, blockStr, receivedLogHeaders, headerStr, truncatedDuration, receivedLogTransactions,
|
|
||||||
txStr, mstime.UnixMilliseconds(block.Header.TimeInMilliseconds()))
|
|
||||||
|
|
||||||
receivedLogBlocks = 0
|
|
||||||
receivedLogHeaders = 0
|
|
||||||
receivedLogTransactions = 0
|
|
||||||
lastBlockLogTime = now
|
|
||||||
}
|
|
||||||
@ -3,6 +3,7 @@ package blockprocessor
|
|||||||
import (
|
import (
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/processes/blockprocessor/blocklogger"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -13,6 +14,7 @@ type blockProcessor struct {
|
|||||||
genesisHash *externalapi.DomainHash
|
genesisHash *externalapi.DomainHash
|
||||||
targetTimePerBlock time.Duration
|
targetTimePerBlock time.Duration
|
||||||
databaseContext model.DBManager
|
databaseContext model.DBManager
|
||||||
|
blockLogger *blocklogger.BlockLogger
|
||||||
|
|
||||||
consensusStateManager model.ConsensusStateManager
|
consensusStateManager model.ConsensusStateManager
|
||||||
pruningManager model.PruningManager
|
pruningManager model.PruningManager
|
||||||
@ -49,6 +51,7 @@ func New(
|
|||||||
genesisHash *externalapi.DomainHash,
|
genesisHash *externalapi.DomainHash,
|
||||||
targetTimePerBlock time.Duration,
|
targetTimePerBlock time.Duration,
|
||||||
databaseContext model.DBManager,
|
databaseContext model.DBManager,
|
||||||
|
|
||||||
consensusStateManager model.ConsensusStateManager,
|
consensusStateManager model.ConsensusStateManager,
|
||||||
pruningManager model.PruningManager,
|
pruningManager model.PruningManager,
|
||||||
blockValidator model.BlockValidator,
|
blockValidator model.BlockValidator,
|
||||||
@ -81,6 +84,7 @@ func New(
|
|||||||
genesisHash: genesisHash,
|
genesisHash: genesisHash,
|
||||||
targetTimePerBlock: targetTimePerBlock,
|
targetTimePerBlock: targetTimePerBlock,
|
||||||
databaseContext: databaseContext,
|
databaseContext: databaseContext,
|
||||||
|
blockLogger: blocklogger.NewBlockLogger(),
|
||||||
pruningManager: pruningManager,
|
pruningManager: pruningManager,
|
||||||
blockValidator: blockValidator,
|
blockValidator: blockValidator,
|
||||||
dagTopologyManager: dagTopologyManager,
|
dagTopologyManager: dagTopologyManager,
|
||||||
|
|||||||
@ -2,7 +2,6 @@ package blockprocessor
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/processes/blockprocessor/blocklogger"
|
|
||||||
"github.com/kaspanet/kaspad/util/difficulty"
|
"github.com/kaspanet/kaspad/util/difficulty"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||||
@ -144,7 +143,7 @@ func (bp *blockProcessor) validateAndInsertBlock(block *externalapi.DomainBlock,
|
|||||||
return nil, logClosureErr
|
return nil, logClosureErr
|
||||||
}
|
}
|
||||||
|
|
||||||
blocklogger.LogBlock(block)
|
bp.blockLogger.LogBlock(block)
|
||||||
|
|
||||||
return &externalapi.BlockInsertionResult{
|
return &externalapi.BlockInsertionResult{
|
||||||
VirtualSelectedParentChainChanges: selectedParentChainChanges,
|
VirtualSelectedParentChainChanges: selectedParentChainChanges,
|
||||||
|
|||||||
@ -1,6 +1,12 @@
|
|||||||
package blockvalidator_test
|
package blockvalidator_test
|
||||||
|
|
||||||
import (
|
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"
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -84,7 +90,7 @@ func TestChainedTransactions(t *testing.T) {
|
|||||||
func TestCheckBlockSanity(t *testing.T) {
|
func TestCheckBlockSanity(t *testing.T) {
|
||||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||||
factory := consensus.NewFactory()
|
factory := consensus.NewFactory()
|
||||||
consensus, teardown, err := factory.NewTestConsensus(params, false, "TestCheckBlockSanity")
|
tc, teardown, err := factory.NewTestConsensus(params, false, "TestCheckBlockSanity")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error setting up consensus: %+v", err)
|
t.Fatalf("Error setting up consensus: %+v", err)
|
||||||
}
|
}
|
||||||
@ -94,17 +100,17 @@ func TestCheckBlockSanity(t *testing.T) {
|
|||||||
t.Fatalf("Too few transactions in block, expect at least 3, got %v", len(exampleValidBlock.Transactions))
|
t.Fatalf("Too few transactions in block, expect at least 3, got %v", len(exampleValidBlock.Transactions))
|
||||||
}
|
}
|
||||||
|
|
||||||
consensus.BlockStore().Stage(blockHash, &exampleValidBlock)
|
tc.BlockStore().Stage(blockHash, &exampleValidBlock)
|
||||||
|
|
||||||
err = consensus.BlockValidator().ValidateBodyInIsolation(blockHash)
|
err = tc.BlockValidator().ValidateBodyInIsolation(blockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed validating block in isolation: %v", err)
|
t.Fatalf("Failed validating block in isolation: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test with block with wrong transactions sorting order
|
// Test with block with wrong transactions sorting order
|
||||||
blockHash = consensushashing.BlockHash(&blockWithWrongTxOrder)
|
blockHash = consensushashing.BlockHash(&blockWithWrongTxOrder)
|
||||||
consensus.BlockStore().Stage(blockHash, &blockWithWrongTxOrder)
|
tc.BlockStore().Stage(blockHash, &blockWithWrongTxOrder)
|
||||||
err = consensus.BlockValidator().ValidateBodyInIsolation(blockHash)
|
err = tc.BlockValidator().ValidateBodyInIsolation(blockHash)
|
||||||
if !errors.Is(err, ruleerrors.ErrTransactionsNotSorted) {
|
if !errors.Is(err, ruleerrors.ErrTransactionsNotSorted) {
|
||||||
t.Errorf("CheckBlockSanity: Expected ErrTransactionsNotSorted error, instead got %v", err)
|
t.Errorf("CheckBlockSanity: Expected ErrTransactionsNotSorted error, instead got %v", err)
|
||||||
}
|
}
|
||||||
@ -112,8 +118,8 @@ func TestCheckBlockSanity(t *testing.T) {
|
|||||||
// Test a block with invalid parents order
|
// Test a block with invalid parents order
|
||||||
// We no longer require blocks to have ordered parents
|
// We no longer require blocks to have ordered parents
|
||||||
blockHash = consensushashing.BlockHash(&unOrderedParentsBlock)
|
blockHash = consensushashing.BlockHash(&unOrderedParentsBlock)
|
||||||
consensus.BlockStore().Stage(blockHash, &unOrderedParentsBlock)
|
tc.BlockStore().Stage(blockHash, &unOrderedParentsBlock)
|
||||||
err = consensus.BlockValidator().ValidateBodyInIsolation(blockHash)
|
err = tc.BlockValidator().ValidateBodyInIsolation(blockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("CheckBlockSanity: Expected block to be be body in isolation valid, got error instead: %v", err)
|
t.Errorf("CheckBlockSanity: Expected block to be be body in isolation valid, got error instead: %v", err)
|
||||||
}
|
}
|
||||||
@ -1034,3 +1040,309 @@ 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,6 +1,7 @@
|
|||||||
package blockvalidator_test
|
package blockvalidator_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/pow"
|
"github.com/kaspanet/kaspad/domain/consensus/model/pow"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
|
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
|
||||||
@ -12,8 +13,8 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus"
|
"github.com/kaspanet/kaspad/domain/consensus"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
@ -241,3 +242,60 @@ 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"
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
// blueBlockWindow returns a blockWindow of the given size that contains the
|
// BlockWindow returns a blockWindow of the given size that contains the
|
||||||
// blues in the past of startindNode, the sorting is unspecified.
|
// blocks in the past of startindNode, the sorting is unspecified.
|
||||||
// If the number of blues in the past of startingNode is less then windowSize,
|
// If the number of blocks in the past of startingNode is less then windowSize,
|
||||||
// the window will be padded by genesis blocks to achieve a size of windowSize.
|
// the window will be padded by genesis blocks to achieve a size of windowSize.
|
||||||
func (dtm *dagTraversalManager) BlueWindow(startingBlock *externalapi.DomainHash, windowSize int) ([]*externalapi.DomainHash, error) {
|
func (dtm *dagTraversalManager) BlockWindow(startingBlock *externalapi.DomainHash, windowSize int) ([]*externalapi.DomainHash, error) {
|
||||||
currentHash := startingBlock
|
currentHash := startingBlock
|
||||||
currentGHOSTDAGData, err := dtm.ghostdagDataStore.Get(dtm.databaseContext, currentHash)
|
currentGHOSTDAGData, err := dtm.ghostdagDataStore.Get(dtm.databaseContext, currentHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBlueBlockWindow(t *testing.T) {
|
func TestBlockWindow(t *testing.T) {
|
||||||
tests := map[string][]*struct {
|
tests := map[string][]*struct {
|
||||||
parents []string
|
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
|
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 TestBlueBlockWindow(t *testing.T) {
|
|||||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||||
params.K = 1
|
params.K = 1
|
||||||
factory := consensus.NewFactory()
|
factory := consensus.NewFactory()
|
||||||
tc, tearDown, err := factory.NewTestConsensus(params, false, "TestBlueBlockWindow")
|
tc, tearDown, err := factory.NewTestConsensus(params, false, "TestBlockWindow")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewTestConsensus: %s", err)
|
t.Fatalf("NewTestConsensus: %s", err)
|
||||||
}
|
}
|
||||||
@ -340,9 +340,9 @@ func TestBlueBlockWindow(t *testing.T) {
|
|||||||
blockByIDMap[blockData.id] = block
|
blockByIDMap[blockData.id] = block
|
||||||
idByBlockMap[*block] = blockData.id
|
idByBlockMap[*block] = blockData.id
|
||||||
|
|
||||||
window, err := tc.DAGTraversalManager().BlueWindow(block, windowSize)
|
window, err := tc.DAGTraversalManager().BlockWindow(block, windowSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("BlueWindow: %s", err)
|
t.Fatalf("BlockWindow: %s", err)
|
||||||
}
|
}
|
||||||
sort.Sort(testutils.NewTestGhostDAGSorter(window, tc, t))
|
sort.Sort(testutils.NewTestGhostDAGSorter(window, tc, t))
|
||||||
if err := checkWindowIDs(window, blockData.expectedWindowWithGenesisPadding, idByBlockMap); err != nil {
|
if err := checkWindowIDs(window, blockData.expectedWindowWithGenesisPadding, idByBlockMap); err != nil {
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
package difficultymanager
|
package difficultymanager
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
||||||
"github.com/kaspanet/kaspad/util/difficulty"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
|
"github.com/kaspanet/kaspad/util/difficulty"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type difficultyBlock struct {
|
type difficultyBlock struct {
|
||||||
@ -27,13 +28,13 @@ func (dm *difficultyManager) getDifficultyBlock(blockHash *externalapi.DomainHas
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// blueBlockWindow returns a blockWindow of the given size that contains the
|
// blockWindow returns a blockWindow of the given size that contains the
|
||||||
// blues in the past of startindNode, the sorting is unspecified.
|
// blocks in the past of startindNode, the sorting is unspecified.
|
||||||
// If the number of blues in the past of startingNode is less then windowSize,
|
// If the number of blocks in the past of startingNode is less then windowSize,
|
||||||
// the window will be padded by genesis blocks to achieve a size of windowSize.
|
// the window will be padded by genesis blocks to achieve a size of windowSize.
|
||||||
func (dm *difficultyManager) blueBlockWindow(startingNode *externalapi.DomainHash, windowSize int) (blockWindow, error) {
|
func (dm *difficultyManager) blockWindow(startingNode *externalapi.DomainHash, windowSize int) (blockWindow, error) {
|
||||||
window := make(blockWindow, 0, windowSize)
|
window := make(blockWindow, 0, windowSize)
|
||||||
windowHashes, err := dm.dagTraversalManager.BlueWindow(startingNode, windowSize)
|
windowHashes, err := dm.dagTraversalManager.BlockWindow(startingNode, windowSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
package difficultymanager
|
package difficultymanager
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kaspanet/kaspad/util/difficulty"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/util/difficulty"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
)
|
)
|
||||||
@ -88,7 +89,7 @@ func (dm *difficultyManager) RequiredDifficulty(blockHash *externalapi.DomainHas
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fetch window of dag.difficultyAdjustmentWindowSize + 1 so we can have dag.difficultyAdjustmentWindowSize block intervals
|
// Fetch window of dag.difficultyAdjustmentWindowSize + 1 so we can have dag.difficultyAdjustmentWindowSize block intervals
|
||||||
targetsWindow, err := dm.blueBlockWindow(bluestParent, dm.difficultyAdjustmentWindowSize+1)
|
targetsWindow, err := dm.blockWindow(bluestParent, dm.difficultyAdjustmentWindowSize+1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -57,7 +57,7 @@ func (pmtm *pastMedianTimeManager) PastMedianTime(blockHash *externalapi.DomainH
|
|||||||
return header.TimeInMilliseconds(), nil
|
return header.TimeInMilliseconds(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
window, err := pmtm.dagTraversalManager.BlueWindow(selectedParentHash, 2*pmtm.timestampDeviationTolerance-1)
|
window, err := pmtm.dagTraversalManager.BlockWindow(selectedParentHash, 2*pmtm.timestampDeviationTolerance-1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,83 @@
|
|||||||
|
package pastmediantimemanager_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"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/utils/testutils"
|
||||||
|
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPastMedianTime(t *testing.T) {
|
||||||
|
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||||
|
factory := consensus.NewFactory()
|
||||||
|
tc, tearDown, err := factory.NewTestConsensus(params, false, "TestUpdateReindexRoot")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewTestConsensus: %s", err)
|
||||||
|
}
|
||||||
|
defer tearDown(false)
|
||||||
|
|
||||||
|
numBlocks := uint32(300)
|
||||||
|
blockHashes := make([]*externalapi.DomainHash, numBlocks)
|
||||||
|
blockHashes[0] = params.GenesisHash
|
||||||
|
blockTime := params.GenesisBlock.Header.TimeInMilliseconds()
|
||||||
|
for i := uint32(1); i < numBlocks; i++ {
|
||||||
|
blockTime += 1000
|
||||||
|
block, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{blockHashes[i-1]}, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("BuildBlockWithParents: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newHeader := block.Header.ToMutable()
|
||||||
|
newHeader.SetTimeInMilliseconds(blockTime)
|
||||||
|
block.Header = newHeader.ToImmutable()
|
||||||
|
_, err = tc.ValidateAndInsertBlock(block)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ValidateAndInsertBlock: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
blockHashes[i] = consensushashing.BlockHash(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
blockNumber uint32
|
||||||
|
expectedMillisecondsSinceGenesis int64
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
blockNumber: 263,
|
||||||
|
expectedMillisecondsSinceGenesis: 130000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
blockNumber: 271,
|
||||||
|
expectedMillisecondsSinceGenesis: 138000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
blockNumber: 241,
|
||||||
|
expectedMillisecondsSinceGenesis: 108000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
blockNumber: 5,
|
||||||
|
expectedMillisecondsSinceGenesis: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
pastMedianTime, err := tc.PastMedianTimeManager().PastMedianTime(blockHashes[test.blockNumber])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("PastMedianTime: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
millisecondsSinceGenesis := pastMedianTime -
|
||||||
|
params.GenesisBlock.Header.TimeInMilliseconds()
|
||||||
|
|
||||||
|
if millisecondsSinceGenesis != test.expectedMillisecondsSinceGenesis {
|
||||||
|
t.Errorf("TestCalcPastMedianTime: expected past median time of block %v to be %v milliseconds "+
|
||||||
|
"from genesis but got %v",
|
||||||
|
test.blockNumber, test.expectedMillisecondsSinceGenesis, millisecondsSinceGenesis)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,14 +1,15 @@
|
|||||||
package syncmanager
|
package syncmanager
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashset"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// antiPastHashesBetween returns the hashes of the blocks between the
|
// antiPastHashesBetween returns the hashes of the blocks between the
|
||||||
// lowHash's antiPast and highHash's antiPast, or up to
|
// lowHash's antiPast and highHash's antiPast, or up to
|
||||||
// `maxBlueScoreDifference`, if non-zero.
|
// `maxBlueScoreDifference`, if non-zero.
|
||||||
|
// The result excludes lowHash and includes highHash. If lowHash == highHash, returns nothing.
|
||||||
func (sm *syncManager) antiPastHashesBetween(lowHash, highHash *externalapi.DomainHash,
|
func (sm *syncManager) antiPastHashesBetween(lowHash, highHash *externalapi.DomainHash,
|
||||||
maxBlueScoreDifference uint64) ([]*externalapi.DomainHash, error) {
|
maxBlueScoreDifference uint64) ([]*externalapi.DomainHash, error) {
|
||||||
|
|
||||||
@ -17,19 +18,10 @@ func (sm *syncManager) antiPastHashesBetween(lowHash, highHash *externalapi.Doma
|
|||||||
// highHash's selectedParentChain.
|
// highHash's selectedParentChain.
|
||||||
// We keep originalLowHash to filter out blocks in it's past later down the road
|
// We keep originalLowHash to filter out blocks in it's past later down the road
|
||||||
originalLowHash := lowHash
|
originalLowHash := lowHash
|
||||||
for {
|
var err error
|
||||||
isInSelectedParentChain, err := sm.dagTopologyManager.IsInSelectedParentChainOf(lowHash, highHash)
|
lowHash, err = sm.findLowHashInHighHashSelectedParentChain(lowHash, highHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
|
||||||
if isInSelectedParentChain {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
lowBlockGHOSTDAGData, err := sm.ghostdagDataStore.Get(sm.databaseContext, lowHash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
lowHash = lowBlockGHOSTDAGData.SelectedParent()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lowBlockGHOSTDAGData, err := sm.ghostdagDataStore.Get(sm.databaseContext, lowHash)
|
lowBlockGHOSTDAGData, err := sm.ghostdagDataStore.Get(sm.databaseContext, lowHash)
|
||||||
@ -55,78 +47,138 @@ func (sm *syncManager) antiPastHashesBetween(lowHash, highHash *externalapi.Doma
|
|||||||
// Using blueScore as an approximation is considered to be
|
// Using blueScore as an approximation is considered to be
|
||||||
// fairly accurate because we presume that most DAG blocks are
|
// fairly accurate because we presume that most DAG blocks are
|
||||||
// blue.
|
// blue.
|
||||||
iterator, err := sm.dagTraversalManager.SelectedChildIterator(highHash, lowHash)
|
highHash, err = sm.findHighHashAccordingToMaxBlueScoreDifference(lowHash, highHash, maxBlueScoreDifference, highBlockGHOSTDAGData, lowBlockGHOSTDAGData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for ok := iterator.First(); ok; ok = iterator.Next() {
|
|
||||||
highHash, err = iterator.Get()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
highBlockGHOSTDAGData, err = sm.ghostdagDataStore.Get(sm.databaseContext, highHash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if highBlockGHOSTDAGData.BlueScore()-lowBlockGHOSTDAGData.BlueScore()+1 > maxBlueScoreDifference {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect every node in highHash's past (including itself) but
|
// Collect all hashes by concatenating the merge-sets of all blocks between highHash and lowHash
|
||||||
// NOT in the lowHash's past (excluding itself) into an up-heap
|
blockHashes := []*externalapi.DomainHash{}
|
||||||
// (a heap sorted by blueScore from lowest to greatest).
|
iterator, err := sm.dagTraversalManager.SelectedChildIterator(highHash, lowHash)
|
||||||
visited := hashset.New()
|
|
||||||
hashesUpHeap := sm.dagTraversalManager.NewUpHeap()
|
|
||||||
queue := sm.dagTraversalManager.NewDownHeap()
|
|
||||||
err = queue.Push(highHash)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for queue.Len() > 0 {
|
for ok := iterator.First(); ok; ok = iterator.Next() {
|
||||||
current := queue.Pop()
|
current, err := iterator.Get()
|
||||||
if visited.Contains(current) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
visited.Add(current)
|
|
||||||
var isCurrentAncestorOfLowHash bool
|
|
||||||
if current == lowHash {
|
|
||||||
isCurrentAncestorOfLowHash = false
|
|
||||||
} else {
|
|
||||||
var err error
|
|
||||||
isCurrentAncestorOfLowHash, err = sm.dagTopologyManager.IsAncestorOf(current, lowHash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if isCurrentAncestorOfLowHash {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Push current to hashesUpHeap if it's not in the past of originalLowHash
|
|
||||||
isInPastOfOriginalLowHash, err := sm.dagTopologyManager.IsAncestorOf(current, originalLowHash)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !isInPastOfOriginalLowHash {
|
// Both blue and red merge sets are topologically sorted, but not the concatenation of the two.
|
||||||
err = hashesUpHeap.Push(current)
|
// We require the blocks to be topologically sorted. In addition, for optimal performance,
|
||||||
if err != nil {
|
// we want the selectedParent to be first.
|
||||||
return nil, err
|
// Since the rest of the merge set is in the anticone of selectedParent, it's position in the list does not
|
||||||
}
|
// matter, even though it's blue score is the highest, we can arbitrarily decide it comes first.
|
||||||
}
|
// Therefore we first append the selectedParent, then the rest of blocks in ghostdag order.
|
||||||
parents, err := sm.dagTopologyManager.Parents(current)
|
sortedMergeSet, err := sm.getSortedMergeSet(current)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, parent := range parents {
|
|
||||||
err := queue.Push(parent)
|
// append to blockHashes all blocks in sortedMergeSet which are not in the past of originalLowHash
|
||||||
|
for _, blockHash := range sortedMergeSet {
|
||||||
|
isInPastOfOriginalLowHash, err := sm.dagTopologyManager.IsAncestorOf(blockHash, originalLowHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if isInPastOfOriginalLowHash {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
blockHashes = append(blockHashes, blockHash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return hashesUpHeap.ToSlice(), nil
|
// The process above doesn't return highHash, so include it explicitly, unless highHash == lowHash
|
||||||
|
if !lowHash.Equal(highHash) {
|
||||||
|
blockHashes = append(blockHashes, highHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
return blockHashes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *syncManager) getSortedMergeSet(current *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
|
||||||
|
currentGhostdagData, err := sm.ghostdagDataStore.Get(sm.databaseContext, current)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
blueMergeSet := currentGhostdagData.MergeSetBlues()
|
||||||
|
redMergeSet := currentGhostdagData.MergeSetReds()
|
||||||
|
sortedMergeSet := make([]*externalapi.DomainHash, 0, len(blueMergeSet)+len(redMergeSet))
|
||||||
|
selectedParent, blueMergeSet := blueMergeSet[0], blueMergeSet[1:]
|
||||||
|
sortedMergeSet = append(sortedMergeSet, selectedParent)
|
||||||
|
i, j := 0, 0
|
||||||
|
for i < len(blueMergeSet) && j < len(redMergeSet) {
|
||||||
|
currentBlue := blueMergeSet[i]
|
||||||
|
currentBlueGhostdagData, err := sm.ghostdagDataStore.Get(sm.databaseContext, currentBlue)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
currentRed := redMergeSet[j]
|
||||||
|
currentRedGhostdagData, err := sm.ghostdagDataStore.Get(sm.databaseContext, currentRed)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if sm.ghostdagManager.Less(currentBlue, currentBlueGhostdagData, currentRed, currentRedGhostdagData) {
|
||||||
|
sortedMergeSet = append(sortedMergeSet, currentBlue)
|
||||||
|
i++
|
||||||
|
} else {
|
||||||
|
sortedMergeSet = append(sortedMergeSet, currentRed)
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sortedMergeSet = append(sortedMergeSet, blueMergeSet[i:]...)
|
||||||
|
sortedMergeSet = append(sortedMergeSet, redMergeSet[j:]...)
|
||||||
|
|
||||||
|
return sortedMergeSet, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *syncManager) findHighHashAccordingToMaxBlueScoreDifference(lowHash *externalapi.DomainHash,
|
||||||
|
highHash *externalapi.DomainHash, maxBlueScoreDifference uint64, highBlockGHOSTDAGData *model.BlockGHOSTDAGData,
|
||||||
|
lowBlockGHOSTDAGData *model.BlockGHOSTDAGData) (*externalapi.DomainHash, error) {
|
||||||
|
|
||||||
|
if highBlockGHOSTDAGData.BlueScore()-lowBlockGHOSTDAGData.BlueScore() <= maxBlueScoreDifference {
|
||||||
|
return highHash, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator, err := sm.dagTraversalManager.SelectedChildIterator(highHash, lowHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for ok := iterator.First(); ok; ok = iterator.Next() {
|
||||||
|
highHashCandidate, err := iterator.Get()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
highBlockGHOSTDAGData, err = sm.ghostdagDataStore.Get(sm.databaseContext, highHashCandidate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if highBlockGHOSTDAGData.BlueScore()-lowBlockGHOSTDAGData.BlueScore() > maxBlueScoreDifference {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
highHash = highHashCandidate
|
||||||
|
}
|
||||||
|
return highHash, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *syncManager) findLowHashInHighHashSelectedParentChain(
|
||||||
|
lowHash *externalapi.DomainHash, highHash *externalapi.DomainHash) (*externalapi.DomainHash, error) {
|
||||||
|
for {
|
||||||
|
isInSelectedParentChain, err := sm.dagTopologyManager.IsInSelectedParentChainOf(lowHash, highHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if isInSelectedParentChain {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
lowBlockGHOSTDAGData, err := sm.ghostdagDataStore.Get(sm.databaseContext, lowHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
lowHash = lowBlockGHOSTDAGData.SelectedParent()
|
||||||
|
}
|
||||||
|
return lowHash, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *syncManager) missingBlockBodyHashes(highHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
|
func (sm *syncManager) missingBlockBodyHashes(highHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
|
||||||
|
|||||||
@ -1,14 +1,15 @@
|
|||||||
package syncmanager_test
|
package syncmanager_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kaspanet/kaspad/domain/consensus"
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
|
||||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
|
||||||
"math"
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||||
|
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSyncManager_GetHashesBetween(t *testing.T) {
|
func TestSyncManager_GetHashesBetween(t *testing.T) {
|
||||||
@ -20,27 +21,40 @@ func TestSyncManager_GetHashesBetween(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer teardown(false)
|
defer teardown(false)
|
||||||
|
|
||||||
upBfsOrder := make([]*externalapi.DomainHash, 0, 30)
|
// Create a DAG with the following structure:
|
||||||
selectedParent := params.GenesisHash
|
// merging block
|
||||||
upBfsOrder = append(upBfsOrder, selectedParent)
|
// / | \
|
||||||
|
// split1 split2 split3
|
||||||
|
// \ | /
|
||||||
|
// merging block
|
||||||
|
// / | \
|
||||||
|
// split1 split2 split3
|
||||||
|
// \ | /
|
||||||
|
// etc.
|
||||||
|
expectedOrder := make([]*externalapi.DomainHash, 0, 40)
|
||||||
|
mergingBlock := params.GenesisHash
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
parents := make([]*externalapi.DomainHash, 0, 3)
|
splitBlocks := make([]*externalapi.DomainHash, 0, 3)
|
||||||
for j := 0; j < 4; j++ {
|
for j := 0; j < 3; j++ {
|
||||||
blockHash, _, err := tc.AddBlock([]*externalapi.DomainHash{selectedParent}, nil, nil)
|
splitBlock, _, err := tc.AddBlock([]*externalapi.DomainHash{mergingBlock}, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed adding block: %v", err)
|
t.Fatalf("Failed adding block: %v", err)
|
||||||
}
|
}
|
||||||
parents = append(parents, blockHash)
|
splitBlocks = append(splitBlocks, splitBlock)
|
||||||
upBfsOrder = append(upBfsOrder, blockHash)
|
|
||||||
}
|
}
|
||||||
selectedParent, _, err = tc.AddBlock(parents, nil, nil)
|
sort.Sort(sort.Reverse(testutils.NewTestGhostDAGSorter(splitBlocks, tc, t)))
|
||||||
|
restOfSplitBlocks, selectedParent := splitBlocks[:len(splitBlocks)-1], splitBlocks[len(splitBlocks)-1]
|
||||||
|
expectedOrder = append(expectedOrder, selectedParent)
|
||||||
|
expectedOrder = append(expectedOrder, restOfSplitBlocks...)
|
||||||
|
|
||||||
|
mergingBlock, _, err = tc.AddBlock(splitBlocks, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed adding block: %v", err)
|
t.Fatalf("Failed adding block: %v", err)
|
||||||
}
|
}
|
||||||
upBfsOrder = append(upBfsOrder, selectedParent)
|
expectedOrder = append(expectedOrder, mergingBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, blockHash := range upBfsOrder {
|
for i, blockHash := range expectedOrder {
|
||||||
empty, err := tc.SyncManager().GetHashesBetween(blockHash, blockHash, math.MaxUint64)
|
empty, err := tc.SyncManager().GetHashesBetween(blockHash, blockHash, math.MaxUint64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("TestSyncManager_GetHashesBetween failed returning 0 hashes on the %d'th block: %v", i, err)
|
t.Fatalf("TestSyncManager_GetHashesBetween failed returning 0 hashes on the %d'th block: %v", i, err)
|
||||||
@ -50,15 +64,13 @@ func TestSyncManager_GetHashesBetween(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allHashes, err := tc.SyncManager().GetHashesBetween(upBfsOrder[0], upBfsOrder[len(upBfsOrder)-1], math.MaxUint64)
|
actualOrder, err := tc.SyncManager().GetHashesBetween(params.GenesisHash, expectedOrder[len(expectedOrder)-1], math.MaxUint64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("TestSyncManager_GetHashesBetween failed returning allHashes: %v", err)
|
t.Fatalf("TestSyncManager_GetHashesBetween failed returning actualOrder: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Sort(sort.Reverse(testutils.NewTestGhostDAGSorter(upBfsOrder, tc, t)))
|
if !reflect.DeepEqual(actualOrder, expectedOrder) {
|
||||||
upBfsOrderExcludingGenesis := upBfsOrder[1:]
|
t.Fatalf("TestSyncManager_GetHashesBetween expected: \n%s\nactual:\n%s\n", expectedOrder, actualOrder)
|
||||||
if !reflect.DeepEqual(allHashes, upBfsOrderExcludingGenesis) {
|
|
||||||
t.Fatalf("TestSyncManager_GetHashesBetween expected %v\n == \n%v", allHashes, upBfsOrder)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,251 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -2,6 +2,8 @@ package consensus
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
|
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
|
||||||
@ -9,7 +11,6 @@ import (
|
|||||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/db/database"
|
"github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"io"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type testConsensus struct {
|
type testConsensus struct {
|
||||||
|
|||||||
79
domain/consensus/test_consensus_render_to_dot.go
Normal file
79
domain/consensus/test_consensus_render_to_dot.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
package consensus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RenderDAGToDot is a helper function for debugging tests.
|
||||||
|
// It requires graphviz installed.
|
||||||
|
func (tc *testConsensus) RenderDAGToDot(filename string) error {
|
||||||
|
dotScript, _ := tc.convertToDot()
|
||||||
|
return renderDotScript(dotScript, filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *testConsensus) convertToDot() (string, error) {
|
||||||
|
var dotScriptBuilder strings.Builder
|
||||||
|
dotScriptBuilder.WriteString("digraph {\n\trankdir = TB; \n")
|
||||||
|
|
||||||
|
edges := []string{}
|
||||||
|
|
||||||
|
blocksIterator, err := tc.blockStore.AllBlockHashesIterator(tc.databaseContext)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for ok := blocksIterator.First(); ok; ok = blocksIterator.Next() {
|
||||||
|
hash, err := blocksIterator.Get()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
dotScriptBuilder.WriteString(fmt.Sprintf("\t\"%s\";\n", hash))
|
||||||
|
|
||||||
|
parents, err := tc.dagTopologyManager.Parents(hash)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, parentHash := range parents {
|
||||||
|
edges = append(edges, fmt.Sprintf("\t\"%s\" -> \"%s\";", hash, parentHash))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dotScriptBuilder.WriteString("\n")
|
||||||
|
|
||||||
|
dotScriptBuilder.WriteString(strings.Join(edges, "\n"))
|
||||||
|
|
||||||
|
dotScriptBuilder.WriteString("\n}")
|
||||||
|
|
||||||
|
return dotScriptBuilder.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderDotScript(dotScript string, filename string) error {
|
||||||
|
command := exec.Command("dot", "-Tsvg")
|
||||||
|
stdin, err := command.StdinPipe()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating stdin pipe: %s", err)
|
||||||
|
}
|
||||||
|
spawn("renderDotScript", func() {
|
||||||
|
defer stdin.Close()
|
||||||
|
|
||||||
|
_, err = io.WriteString(stdin, dotScript)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("Error writing dotScript into stdin pipe: %s", err))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
command.Stderr = &stderr
|
||||||
|
svg, err := command.Output()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error getting output of dot: %s\nstderr:\n%s", err, stderr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return ioutil.WriteFile(filename, svg, 0600)
|
||||||
|
}
|
||||||
@ -1,130 +1,131 @@
|
|||||||
package miningmanager_test
|
package miningmanager_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
//"bytes"
|
||||||
"github.com/kaspanet/kaspad/app/appmessage"
|
//"github.com/kaspanet/kaspad/app/appmessage"
|
||||||
|
"github.com/kaspanet/kaspad/domain"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus"
|
"github.com/kaspanet/kaspad/domain/consensus"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
"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/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/constants"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
//"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
|
"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/consensus/utils/txscript"
|
||||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||||
"github.com/kaspanet/kaspad/domain/miningmanager"
|
//"github.com/kaspanet/kaspad/domain/miningmanager"
|
||||||
infrastructuredatabase "github.com/kaspanet/kaspad/infrastructure/db/database"
|
//infrastructuredatabase "github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/db/database/ldb"
|
//"github.com/kaspanet/kaspad/infrastructure/db/database/ldb"
|
||||||
"github.com/kaspanet/kaspad/util"
|
"github.com/kaspanet/kaspad/util"
|
||||||
"github.com/pkg/errors"
|
//"github.com/pkg/errors"
|
||||||
"github.com/syndtr/goleveldb/leveldb/opt"
|
//"github.com/syndtr/goleveldb/leveldb/opt"
|
||||||
"io/ioutil"
|
//"io/ioutil"
|
||||||
"os"
|
//"os"
|
||||||
"path/filepath"
|
//"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupDBForTest(dbName string) (infrastructuredatabase.Database, func(), error) {
|
//func setupDBForTest(dbName string) (infrastructuredatabase.Database, func(), error) {
|
||||||
var err error
|
// var err error
|
||||||
tmpDir, err := ioutil.TempDir("", "setupDBManager")
|
// tmpDir, err := ioutil.TempDir("", "setupDBManager")
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return nil, nil, errors.Errorf("error creating temp dir: %s", err)
|
// return nil, nil, errors.Errorf("error creating temp dir: %s", err)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
dbPath := filepath.Join(tmpDir, dbName)
|
// dbPath := filepath.Join(tmpDir, dbName)
|
||||||
_ = os.RemoveAll(dbPath)
|
// _ = os.RemoveAll(dbPath)
|
||||||
db, err := ldb.NewLevelDB(dbPath)
|
// db, err := ldb.NewLevelDB(dbPath)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return nil, nil, err
|
// return nil, nil, err
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
originalLDBOptions := ldb.Options
|
// originalLDBOptions := ldb.Options
|
||||||
ldb.Options = func() *opt.Options {
|
// ldb.Options = func() *opt.Options {
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
teardown := func() {
|
// teardown := func() {
|
||||||
db.Close()
|
// db.Close()
|
||||||
ldb.Options = originalLDBOptions
|
// ldb.Options = originalLDBOptions
|
||||||
os.RemoveAll(dbPath)
|
// os.RemoveAll(dbPath)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
return db, teardown, err
|
// return db, teardown, err
|
||||||
}
|
//}
|
||||||
|
|
||||||
func createCoinbaseTransaction(t *testing.T, scriptPublicKey []byte, value uint64) *externalapi.DomainTransaction {
|
func createCoinbaseTransaction(t *testing.T, scriptPublicKey []byte, value uint64) *externalapi.DomainTransaction {
|
||||||
dummyTxOut := externalapi.DomainTransactionOutput{
|
dummyTxOut := externalapi.DomainTransactionOutput{
|
||||||
Value: value,
|
Value: value,
|
||||||
ScriptPublicKey: scriptPublicKey,
|
ScriptPublicKey: nil,
|
||||||
}
|
|
||||||
payload, err := coinbase.SerializeCoinbasePayload(1, &externalapi.DomainCoinbaseData{
|
|
||||||
ScriptPublicKey:scriptPublicKey,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("SerializeCoinbasePayload: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
payloadHash := hashes.HashData(payload)
|
|
||||||
transaction := &externalapi.DomainTransaction{
|
transaction := &externalapi.DomainTransaction{
|
||||||
Version: constants.TransactionVersion,
|
Version: constants.MaxTransactionVersion,
|
||||||
Inputs: []*externalapi.DomainTransactionInput{},
|
Inputs: []*externalapi.DomainTransactionInput{},
|
||||||
Outputs: []*externalapi.DomainTransactionOutput{&dummyTxOut},
|
Outputs: []*externalapi.DomainTransactionOutput{&dummyTxOut},
|
||||||
LockTime: 0,
|
LockTime: 0,
|
||||||
SubnetworkID: subnetworks.SubnetworkIDCoinbase,
|
SubnetworkID: subnetworks.SubnetworkIDCoinbase,
|
||||||
Gas: 0,
|
|
||||||
PayloadHash: *payloadHash,
|
|
||||||
Payload: payload,
|
|
||||||
}
|
|
||||||
|
|
||||||
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.TransactionVersion,
|
|
||||||
Inputs: inputs,
|
|
||||||
Outputs: []*externalapi.DomainTransactionOutput{&dummyTxOut},
|
|
||||||
LockTime: 0,
|
|
||||||
SubnetworkID: subnetworks.SubnetworkIDNative,
|
|
||||||
Gas: 0,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return transaction
|
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) {
|
func TestMiningManager(t *testing.T) {
|
||||||
dagParams := &dagconfig.SimnetParams
|
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||||
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}
|
factory := consensus.NewFactory()
|
||||||
miningAddr, err := util.NewAddressPubKeyHash(miningAddrHash[:], util.Bech32PrefixKaspaTest)
|
tc, teardown, err := factory.NewTestConsensus(params, false, "TestBlockSize")
|
||||||
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 {
|
if err != nil {
|
||||||
t.Fatalf("NewConsensus: %v", err)
|
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
|
// Insert 10 transactions
|
||||||
miningManager := miningManagerFactory.NewMiningManager(consensusInstance, constants.MaxMassAcceptedByBlock)
|
miningManager := domain.MiningManager()
|
||||||
|
//miningManager := miningManagerFactory.NewMiningManager(consensusInstance, constants.MaxMassAcceptedByBlock)
|
||||||
transactions := make([]*externalapi.DomainTransaction, 10)
|
transactions := make([]*externalapi.DomainTransaction, 10)
|
||||||
for i := range transactions {
|
for i := range transactions {
|
||||||
transaction := createCoinbaseTransaction(t, scriptPublicKey, uint64(100000000+i))
|
transaction := createCoinbaseTransaction(t, scriptPublicKey.Script, uint64(100000000+i))
|
||||||
transactions[i] = transaction
|
transactions[i] = transaction
|
||||||
err = miningManager.ValidateAndInsertTransaction(transaction, true)
|
err = miningManager.ValidateAndInsertTransaction(transaction, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -134,9 +135,12 @@ func TestMiningManager(t *testing.T) {
|
|||||||
|
|
||||||
// Spending 10 transactions
|
// Spending 10 transactions
|
||||||
miningManager.HandleNewBlockTransactions(transactions)
|
miningManager.HandleNewBlockTransactions(transactions)
|
||||||
block := miningManager.GetBlockTemplate(&externalapi.DomainCoinbaseData{
|
block, err := miningManager.GetBlockTemplate(&externalapi.DomainCoinbaseData{
|
||||||
ScriptPublicKey: scriptPublicKey,
|
ScriptPublicKey: scriptPublicKey,
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
// todo
|
||||||
|
}
|
||||||
if block == nil {
|
if block == nil {
|
||||||
t.Fatalf("GetBlockTemplate: failed building block")
|
t.Fatalf("GetBlockTemplate: failed building block")
|
||||||
}
|
}
|
||||||
@ -144,122 +148,179 @@ func TestMiningManager(t *testing.T) {
|
|||||||
// Check 10 transactions are not exist
|
// Check 10 transactions are not exist
|
||||||
for _, tx2 := range transactions {
|
for _, tx2 := range transactions {
|
||||||
for _, tx1 := range block.Transactions {
|
for _, tx1 := range block.Transactions {
|
||||||
if consensusserialization.TransactionID(tx1) == consensusserialization.TransactionID(tx2) {
|
if consensushashing.TransactionID(tx1) == consensushashing.TransactionID(tx2) {
|
||||||
t.Fatalf("Spent tranasaction is still exisit")
|
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)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//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)
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
//}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user