mirror of
https://github.com/kaspanet/kaspad.git
synced 2026-02-21 11:17:05 +00:00
Compare commits
146 Commits
v0.8.9-dev
...
testsCheck
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1175a8afe7 | ||
|
|
7e6eaf9299 | ||
|
|
79c74c482b | ||
|
|
3b0394eefe | ||
|
|
b9339e8815 | ||
|
|
43e6467ff1 | ||
|
|
363494ef7a | ||
|
|
8aadb0bd44 | ||
|
|
092177d9a5 | ||
|
|
d1df97c4c5 | ||
|
|
4f52a6de51 | ||
|
|
55ee5f3115 | ||
|
|
3084e3a366 | ||
|
|
4f4a8934e7 | ||
|
|
16ba2bd312 | ||
|
|
6613faee2d | ||
|
|
edc459ae1b | ||
|
|
d7f2cf81c0 | ||
|
|
4658f9d05c | ||
|
|
010df3b0d3 | ||
|
|
346598e67f | ||
|
|
268906a7ce | ||
|
|
befc60b185 | ||
|
|
dd3e04e671 | ||
|
|
9c743db4d6 | ||
|
|
eb3dba5c88 | ||
|
|
e46e2580b1 | ||
|
|
414f58fb90 | ||
|
|
9df80957b1 | ||
|
|
268c9fa83c | ||
|
|
2e3592e351 | ||
|
|
19718ac102 | ||
|
|
28a8e96e65 | ||
|
|
4df283934a | ||
|
|
ab89efe3dc | ||
|
|
fa16c30cf3 | ||
|
|
c28366eb50 | ||
|
|
dc0bf56bf3 | ||
|
|
91de1807ad | ||
|
|
830684167c | ||
|
|
1f56a68a28 | ||
|
|
13a6b4cc51 | ||
|
|
dfd8b3423d | ||
|
|
28bfc0fb9c | ||
|
|
83beae4463 | ||
|
|
a6ebe83198 | ||
|
|
acdc59b565 | ||
|
|
15811b0bcb | ||
|
|
a8a7e3dd9b | ||
|
|
3f193e9219 | ||
|
|
dfa24d8353 | ||
|
|
3c3ad1425d | ||
|
|
9bb8123391 | ||
|
|
347dd8fc4b | ||
|
|
d2cccd2829 | ||
|
|
7186f83095 | ||
|
|
5c394c2951 | ||
|
|
a786cdc15e | ||
|
|
6dd3d4a9e7 | ||
|
|
73b36f12f0 | ||
|
|
a795a9e619 | ||
|
|
0be1bba408 | ||
|
|
6afc06ce58 | ||
|
|
d01a213f3d | ||
|
|
7ad8ce521c | ||
|
|
86ba80a091 | ||
|
|
088e2114c2 | ||
|
|
2854d91688 | ||
|
|
af10b59181 | ||
|
|
c5b0394bbc | ||
|
|
9266d179a9 | ||
|
|
321792778e | ||
|
|
70f3fa9893 | ||
|
|
4e18031483 | ||
|
|
2abc284e3b | ||
|
|
f1451406f7 | ||
|
|
c12e180873 | ||
|
|
3959bc1e7c | ||
|
|
6ec0a8a559 | ||
|
|
6824be9216 | ||
|
|
d0511c1636 | ||
|
|
7d69b66c7c | ||
|
|
cebcab7f5c | ||
|
|
caf251b7a8 | ||
|
|
1a4161ffc0 | ||
|
|
b84080f3d9 | ||
|
|
cbd0bb6d14 | ||
|
|
d9449a32b8 | ||
|
|
0ee8f2b631 | ||
|
|
ff1c96c149 | ||
|
|
5e335be5ab | ||
|
|
032eda4604 | ||
|
|
e4e3541a30 | ||
|
|
b5933bc4fe | ||
|
|
ec446ac511 | ||
|
|
3d668cc1bd | ||
|
|
1486a6312c | ||
|
|
cd27f2850e | ||
|
|
14cf7f81f3 | ||
|
|
74539f8f0b | ||
|
|
a7299c1b87 | ||
|
|
27c1e4611e | ||
|
|
b8413fcecb | ||
|
|
c084c69771 | ||
|
|
53781eed4d | ||
|
|
837fa65735 | ||
|
|
dd3b2cf7d1 | ||
|
|
3fd324ca28 | ||
|
|
0271858f25 | ||
|
|
7909480757 | ||
|
|
18274c236b | ||
|
|
7829a9fd76 | ||
|
|
1548ed9629 | ||
|
|
32cd643e8b | ||
|
|
05df6e3e4e | ||
|
|
ce326b3c7d | ||
|
|
a4ae263ed5 | ||
|
|
79be1edaa5 | ||
|
|
c1ef5f0c56 | ||
|
|
458e409654 | ||
|
|
df19bdfaf3 | ||
|
|
103edf97d0 | ||
|
|
1f69f9eed9 | ||
|
|
6a12428504 | ||
|
|
63b1d2a05a | ||
|
|
089115c389 | ||
|
|
24a12cf2a1 | ||
|
|
6597f24bab | ||
|
|
dc84913214 | ||
|
|
201646282b | ||
|
|
3f08bf87a8 | ||
|
|
581a12db96 | ||
|
|
fb6c9c8f21 | ||
|
|
2adb4f5d0f | ||
|
|
9ffda2b1da | ||
|
|
bee0893660 | ||
|
|
a7bb1853f9 | ||
|
|
35e555e959 | ||
|
|
a250f697ee | ||
|
|
f66708b3c6 | ||
|
|
8fbea5d239 | ||
|
|
5fa06fe7d7 | ||
|
|
06fd6f1b95 | ||
|
|
d2f4ed660c | ||
|
|
19878aa062 | ||
|
|
6415e525c3 |
77
.github/workflows/go-deploy.yml
vendored
Normal file
77
.github/workflows/go-deploy.yml
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
name: Build and Upload assets
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ ubuntu-latest, windows-latest, macos-latest ]
|
||||
name: Building For ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Fix windows CRLF
|
||||
run: git config --global core.autocrlf false
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# We need to increase the page size because the tests run out of memory on github CI windows.
|
||||
# Use the powershell script from this github action: https://github.com/al-cheb/configure-pagefile-action/blob/master/scripts/SetPageFileSize.ps1
|
||||
# MIT License (MIT) Copyright (c) 2020 Maxim Lobanov and contributors
|
||||
- name: Increase page size on windows
|
||||
if: runner.os == 'Windows'
|
||||
shell: powershell
|
||||
run: powershell -command .\.github\workflows\SetPageFileSize.ps1
|
||||
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
|
||||
- name: Build on linux
|
||||
if: runner.os == 'Linux'
|
||||
# `-extldflags=-static` - means static link everything, `-tags netgo,osusergo` means use pure go replacements for "os/user" and "net"
|
||||
# `-s -w` strips the binary to produce smaller size binaries
|
||||
run: |
|
||||
go build -v -ldflags="-s -w -extldflags=-static" -tags netgo,osusergo -o ./bin/ ./...
|
||||
archive="bin/kaspad-${{ github.event.release.tag_name }}-linux.zip"
|
||||
asset_name="kaspad-${{ github.event.release.tag_name }}-linux.zip"
|
||||
zip -r "${archive}" ./bin/*
|
||||
echo "archive=${archive}" >> $GITHUB_ENV
|
||||
echo "asset_name=${asset_name}" >> $GITHUB_ENV
|
||||
|
||||
- name: Build on Windows
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
go build -v -ldflags="-s -w" -o bin/ ./...
|
||||
archive="bin/kaspad-${{ github.event.release.tag_name }}-win64.zip"
|
||||
asset_name="kaspad-${{ github.event.release.tag_name }}-win64.zip"
|
||||
powershell "Compress-Archive bin/* \"${archive}\""
|
||||
echo "archive=${archive}" >> $GITHUB_ENV
|
||||
echo "asset_name=${asset_name}" >> $GITHUB_ENV
|
||||
|
||||
- name: Build on MacOS
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
go build -v -ldflags="-s -w" -o ./bin/ ./...
|
||||
archive="bin/kaspad-${{ github.event.release.tag_name }}-osx.zip"
|
||||
asset_name="kaspad-${{ github.event.release.tag_name }}-osx.zip"
|
||||
zip -r "${archive}" ./bin/*
|
||||
echo "archive=${archive}" >> $GITHUB_ENV
|
||||
echo "asset_name=${asset_name}" >> $GITHUB_ENV
|
||||
|
||||
|
||||
- name: Upload Release Asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ github.event.release.upload_url }}
|
||||
asset_path: "./${{ env.archive }}"
|
||||
asset_name: "${{ env.asset_name }}"
|
||||
asset_content_type: application/zip
|
||||
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 ./...
|
||||
14
.github/workflows/go.yml
vendored
14
.github/workflows/go.yml
vendored
@@ -11,8 +11,9 @@ jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ ubuntu-16.04, macos-10.15 ]
|
||||
os: [ ubuntu-16.04, macos-10.15, windows-2019 ]
|
||||
name: Testing on on ${{ matrix.os }}
|
||||
steps:
|
||||
|
||||
@@ -34,7 +35,7 @@ jobs:
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.15
|
||||
go-version: 1.16
|
||||
|
||||
|
||||
# Source: https://github.com/actions/cache/blob/main/examples.md#go---modules
|
||||
@@ -48,7 +49,7 @@ jobs:
|
||||
|
||||
- name: Test
|
||||
shell: bash
|
||||
run: ./build_and_test.sh
|
||||
run: ./build_and_test.sh -v
|
||||
|
||||
coverage:
|
||||
runs-on: ubuntu-20.04
|
||||
@@ -60,10 +61,13 @@ jobs:
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.15
|
||||
go-version: 1.16
|
||||
|
||||
- name: Delete the stability tests from coverage
|
||||
run: rm -r stability-tests
|
||||
|
||||
- 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
|
||||
run: bash <(curl -s https://codecov.io/bash)
|
||||
@@ -18,7 +18,7 @@ Kaspa is an attempt at a proof-of-work cryptocurrency with instant confirmations
|
||||
|
||||
## Requirements
|
||||
|
||||
Go 1.15 or later.
|
||||
Go 1.16 or later.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -56,13 +56,15 @@ $ kaspad
|
||||
```
|
||||
|
||||
## Discord
|
||||
Join our discord server using the following link: https://discord.gg/WmGhhzk
|
||||
Join our discord server using the following link: https://discord.gg/YNYnNN5Pf2
|
||||
|
||||
## Issue Tracker
|
||||
|
||||
The [integrated github issue tracker](https://github.com/kaspanet/kaspad/issues)
|
||||
is used for this project.
|
||||
|
||||
Issue priorities may be seen at https://github.com/orgs/kaspanet/projects/4
|
||||
|
||||
## Documentation
|
||||
|
||||
The [documentation](https://github.com/kaspanet/docs) is a work-in-progress
|
||||
|
||||
42
app/app.go
42
app/app.go
@@ -7,19 +7,17 @@ import (
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||
"github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||
"github.com/kaspanet/kaspad/infrastructure/db/database/ldb"
|
||||
|
||||
"github.com/kaspanet/kaspad/infrastructure/os/signal"
|
||||
"github.com/kaspanet/kaspad/util/profiling"
|
||||
"github.com/kaspanet/kaspad/version"
|
||||
|
||||
"github.com/kaspanet/kaspad/util/panics"
|
||||
|
||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/infrastructure/os/execenv"
|
||||
"github.com/kaspanet/kaspad/infrastructure/os/limits"
|
||||
"github.com/kaspanet/kaspad/infrastructure/os/signal"
|
||||
"github.com/kaspanet/kaspad/infrastructure/os/winservice"
|
||||
"github.com/kaspanet/kaspad/util/panics"
|
||||
"github.com/kaspanet/kaspad/util/profiling"
|
||||
"github.com/kaspanet/kaspad/version"
|
||||
)
|
||||
|
||||
const leveldbCacheSizeMiB = 256
|
||||
@@ -51,6 +49,7 @@ func StartApp() error {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return err
|
||||
}
|
||||
defer logger.BackendLog.Close()
|
||||
defer panics.HandlePanic(log, "MAIN", nil)
|
||||
|
||||
app := &kaspadApp{cfg: cfg}
|
||||
@@ -86,12 +85,6 @@ func (app *kaspadApp) main(startedChan chan<- struct{}) error {
|
||||
profiling.Start(app.cfg.Profile, log)
|
||||
}
|
||||
|
||||
// Perform upgrades to kaspad as new versions require it.
|
||||
if err := doUpgrades(); err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Return now if an interrupt signal was triggered.
|
||||
if signal.InterruptRequested(interrupt) {
|
||||
return nil
|
||||
@@ -164,15 +157,9 @@ func (app *kaspadApp) main(startedChan chan<- struct{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// doUpgrades performs upgrades to kaspad as new versions require it.
|
||||
// currently it's a placeholder we got from kaspad upstream, that does nothing
|
||||
func doUpgrades() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// dbPath returns the path to the block database given a database type.
|
||||
func databasePath(cfg *config.Config) string {
|
||||
return filepath.Join(cfg.DataDir, "db")
|
||||
return filepath.Join(cfg.AppDir, "data")
|
||||
}
|
||||
|
||||
func removeDatabase(cfg *config.Config) error {
|
||||
@@ -182,6 +169,17 @@ func removeDatabase(cfg *config.Config) error {
|
||||
|
||||
func openDB(cfg *config.Config) (database.Database, error) {
|
||||
dbPath := databasePath(cfg)
|
||||
|
||||
err := checkDatabaseVersion(dbPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Infof("Loading database from '%s'", dbPath)
|
||||
return ldb.NewLevelDB(dbPath, leveldbCacheSizeMiB)
|
||||
db, err := ldb.NewLevelDB(dbPath, leveldbCacheSizeMiB)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package appmessage
|
||||
import (
|
||||
"encoding/hex"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
@@ -83,7 +84,6 @@ func DomainTransactionToMsgTx(domainTransaction *externalapi.DomainTransaction)
|
||||
LockTime: domainTransaction.LockTime,
|
||||
SubnetworkID: domainTransaction.SubnetworkID,
|
||||
Gas: domainTransaction.Gas,
|
||||
PayloadHash: domainTransaction.PayloadHash,
|
||||
Payload: domainTransaction.Payload,
|
||||
}
|
||||
}
|
||||
@@ -133,7 +133,6 @@ func MsgTxToDomainTransaction(msgTx *MsgTx) *externalapi.DomainTransaction {
|
||||
LockTime: msgTx.LockTime,
|
||||
SubnetworkID: msgTx.SubnetworkID,
|
||||
Gas: msgTx.Gas,
|
||||
PayloadHash: msgTx.PayloadHash,
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
@@ -164,18 +163,10 @@ func outpointToDomainOutpoint(outpoint *Outpoint) *externalapi.DomainOutpoint {
|
||||
func RPCTransactionToDomainTransaction(rpcTransaction *RPCTransaction) (*externalapi.DomainTransaction, error) {
|
||||
inputs := make([]*externalapi.DomainTransactionInput, len(rpcTransaction.Inputs))
|
||||
for i, input := range rpcTransaction.Inputs {
|
||||
transactionIDBytes, err := hex.DecodeString(input.PreviousOutpoint.TransactionID)
|
||||
previousOutpoint, err := RPCOutpointToDomainOutpoint(input.PreviousOutpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transactionID, err := transactionid.FromBytes(transactionIDBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
previousOutpoint := &externalapi.DomainOutpoint{
|
||||
TransactionID: *transactionID,
|
||||
Index: input.PreviousOutpoint.Index,
|
||||
}
|
||||
signatureScript, err := hex.DecodeString(input.SignatureScript)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -198,19 +189,7 @@ func RPCTransactionToDomainTransaction(rpcTransaction *RPCTransaction) (*externa
|
||||
}
|
||||
}
|
||||
|
||||
subnetworkIDBytes, err := hex.DecodeString(rpcTransaction.SubnetworkID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
subnetworkID, err := subnetworks.FromBytes(subnetworkIDBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payloadHashBytes, err := hex.DecodeString(rpcTransaction.PayloadHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payloadHash, err := externalapi.NewDomainHashFromByteSlice(payloadHashBytes)
|
||||
subnetworkID, err := subnetworks.FromString(rpcTransaction.SubnetworkID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -226,11 +205,40 @@ func RPCTransactionToDomainTransaction(rpcTransaction *RPCTransaction) (*externa
|
||||
LockTime: rpcTransaction.LockTime,
|
||||
SubnetworkID: *subnetworkID,
|
||||
Gas: rpcTransaction.LockTime,
|
||||
PayloadHash: *payloadHash,
|
||||
Payload: payload,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RPCOutpointToDomainOutpoint converts RPCOutpoint to DomainOutpoint
|
||||
func RPCOutpointToDomainOutpoint(outpoint *RPCOutpoint) (*externalapi.DomainOutpoint, error) {
|
||||
transactionID, err := transactionid.FromString(outpoint.TransactionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &externalapi.DomainOutpoint{
|
||||
TransactionID: *transactionID,
|
||||
Index: outpoint.Index,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RPCUTXOEntryToUTXOEntry converts RPCUTXOEntry to UTXOEntry
|
||||
func RPCUTXOEntryToUTXOEntry(entry *RPCUTXOEntry) (externalapi.UTXOEntry, error) {
|
||||
script, err := hex.DecodeString(entry.ScriptPublicKey.Script)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return utxo.NewUTXOEntry(
|
||||
entry.Amount,
|
||||
&externalapi.ScriptPublicKey{
|
||||
Script: script,
|
||||
Version: entry.ScriptPublicKey.Version,
|
||||
},
|
||||
entry.IsCoinbase,
|
||||
entry.BlockDAAScore,
|
||||
), nil
|
||||
}
|
||||
|
||||
// DomainTransactionToRPCTransaction converts DomainTransactions to RPCTransactions
|
||||
func DomainTransactionToRPCTransaction(transaction *externalapi.DomainTransaction) *RPCTransaction {
|
||||
inputs := make([]*RPCTransactionInput, len(transaction.Inputs))
|
||||
@@ -255,8 +263,7 @@ func DomainTransactionToRPCTransaction(transaction *externalapi.DomainTransactio
|
||||
ScriptPublicKey: &RPCScriptPublicKey{Script: scriptPublicKey, Version: output.ScriptPublicKey.Version},
|
||||
}
|
||||
}
|
||||
subnetworkID := hex.EncodeToString(transaction.SubnetworkID[:])
|
||||
payloadHash := transaction.PayloadHash.String()
|
||||
subnetworkID := transaction.SubnetworkID.String()
|
||||
payload := hex.EncodeToString(transaction.Payload)
|
||||
return &RPCTransaction{
|
||||
Version: transaction.Version,
|
||||
@@ -265,7 +272,6 @@ func DomainTransactionToRPCTransaction(transaction *externalapi.DomainTransactio
|
||||
LockTime: transaction.LockTime,
|
||||
SubnetworkID: subnetworkID,
|
||||
Gas: transaction.LockTime,
|
||||
PayloadHash: payloadHash,
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
@@ -277,22 +283,27 @@ func OutpointAndUTXOEntryPairsToDomainOutpointAndUTXOEntryPairs(
|
||||
|
||||
domainOutpointAndUTXOEntryPairs := make([]*externalapi.OutpointAndUTXOEntryPair, len(outpointAndUTXOEntryPairs))
|
||||
for i, outpointAndUTXOEntryPair := range outpointAndUTXOEntryPairs {
|
||||
domainOutpointAndUTXOEntryPairs[i] = &externalapi.OutpointAndUTXOEntryPair{
|
||||
Outpoint: &externalapi.DomainOutpoint{
|
||||
TransactionID: outpointAndUTXOEntryPair.Outpoint.TxID,
|
||||
Index: outpointAndUTXOEntryPair.Outpoint.Index,
|
||||
},
|
||||
UTXOEntry: utxo.NewUTXOEntry(
|
||||
outpointAndUTXOEntryPair.UTXOEntry.Amount,
|
||||
outpointAndUTXOEntryPair.UTXOEntry.ScriptPublicKey,
|
||||
outpointAndUTXOEntryPair.UTXOEntry.IsCoinbase,
|
||||
outpointAndUTXOEntryPair.UTXOEntry.BlockBlueScore,
|
||||
),
|
||||
}
|
||||
domainOutpointAndUTXOEntryPairs[i] = outpointAndUTXOEntryPairToDomainOutpointAndUTXOEntryPair(outpointAndUTXOEntryPair)
|
||||
}
|
||||
return domainOutpointAndUTXOEntryPairs
|
||||
}
|
||||
|
||||
func outpointAndUTXOEntryPairToDomainOutpointAndUTXOEntryPair(
|
||||
outpointAndUTXOEntryPair *OutpointAndUTXOEntryPair) *externalapi.OutpointAndUTXOEntryPair {
|
||||
return &externalapi.OutpointAndUTXOEntryPair{
|
||||
Outpoint: &externalapi.DomainOutpoint{
|
||||
TransactionID: outpointAndUTXOEntryPair.Outpoint.TxID,
|
||||
Index: outpointAndUTXOEntryPair.Outpoint.Index,
|
||||
},
|
||||
UTXOEntry: utxo.NewUTXOEntry(
|
||||
outpointAndUTXOEntryPair.UTXOEntry.Amount,
|
||||
outpointAndUTXOEntryPair.UTXOEntry.ScriptPublicKey,
|
||||
outpointAndUTXOEntryPair.UTXOEntry.IsCoinbase,
|
||||
outpointAndUTXOEntryPair.UTXOEntry.BlockDAAScore,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// DomainOutpointAndUTXOEntryPairsToOutpointAndUTXOEntryPairs converts
|
||||
// domain OutpointAndUTXOEntryPairs to OutpointAndUTXOEntryPairs
|
||||
func DomainOutpointAndUTXOEntryPairsToOutpointAndUTXOEntryPairs(
|
||||
@@ -309,9 +320,76 @@ func DomainOutpointAndUTXOEntryPairsToOutpointAndUTXOEntryPairs(
|
||||
Amount: outpointAndUTXOEntryPair.UTXOEntry.Amount(),
|
||||
ScriptPublicKey: outpointAndUTXOEntryPair.UTXOEntry.ScriptPublicKey(),
|
||||
IsCoinbase: outpointAndUTXOEntryPair.UTXOEntry.IsCoinbase(),
|
||||
BlockBlueScore: outpointAndUTXOEntryPair.UTXOEntry.BlockBlueScore(),
|
||||
BlockDAAScore: outpointAndUTXOEntryPair.UTXOEntry.BlockDAAScore(),
|
||||
},
|
||||
}
|
||||
}
|
||||
return domainOutpointAndUTXOEntryPairs
|
||||
}
|
||||
|
||||
// DomainBlockToRPCBlock converts DomainBlocks to RPCBlocks
|
||||
func DomainBlockToRPCBlock(block *externalapi.DomainBlock) *RPCBlock {
|
||||
header := &RPCBlockHeader{
|
||||
Version: uint32(block.Header.Version()),
|
||||
ParentHashes: hashes.ToStrings(block.Header.ParentHashes()),
|
||||
HashMerkleRoot: block.Header.HashMerkleRoot().String(),
|
||||
AcceptedIDMerkleRoot: block.Header.AcceptedIDMerkleRoot().String(),
|
||||
UTXOCommitment: block.Header.UTXOCommitment().String(),
|
||||
Timestamp: block.Header.TimeInMilliseconds(),
|
||||
Bits: block.Header.Bits(),
|
||||
Nonce: block.Header.Nonce(),
|
||||
}
|
||||
transactions := make([]*RPCTransaction, len(block.Transactions))
|
||||
for i, transaction := range block.Transactions {
|
||||
transactions[i] = DomainTransactionToRPCTransaction(transaction)
|
||||
}
|
||||
return &RPCBlock{
|
||||
Header: header,
|
||||
Transactions: transactions,
|
||||
}
|
||||
}
|
||||
|
||||
// RPCBlockToDomainBlock converts `block` into a DomainBlock
|
||||
func RPCBlockToDomainBlock(block *RPCBlock) (*externalapi.DomainBlock, error) {
|
||||
parentHashes := make([]*externalapi.DomainHash, len(block.Header.ParentHashes))
|
||||
for i, parentHash := range block.Header.ParentHashes {
|
||||
domainParentHashes, err := externalapi.NewDomainHashFromString(parentHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parentHashes[i] = domainParentHashes
|
||||
}
|
||||
hashMerkleRoot, err := externalapi.NewDomainHashFromString(block.Header.HashMerkleRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
acceptedIDMerkleRoot, err := externalapi.NewDomainHashFromString(block.Header.AcceptedIDMerkleRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
utxoCommitment, err := externalapi.NewDomainHashFromString(block.Header.UTXOCommitment)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
header := blockheader.NewImmutableBlockHeader(
|
||||
uint16(block.Header.Version),
|
||||
parentHashes,
|
||||
hashMerkleRoot,
|
||||
acceptedIDMerkleRoot,
|
||||
utxoCommitment,
|
||||
block.Header.Timestamp,
|
||||
block.Header.Bits,
|
||||
block.Header.Nonce)
|
||||
transactions := make([]*externalapi.DomainTransaction, len(block.Transactions))
|
||||
for i, transaction := range block.Transactions {
|
||||
domainTransaction, err := RPCTransactionToDomainTransaction(transaction)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transactions[i] = domainTransaction
|
||||
}
|
||||
return &externalapi.DomainBlock{
|
||||
Header: header,
|
||||
Transactions: transactions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -132,6 +132,16 @@ const (
|
||||
CmdUnbanResponseMessage
|
||||
CmdGetInfoRequestMessage
|
||||
CmdGetInfoResponseMessage
|
||||
CmdNotifyPruningPointUTXOSetOverrideRequestMessage
|
||||
CmdNotifyPruningPointUTXOSetOverrideResponseMessage
|
||||
CmdPruningPointUTXOSetOverrideNotificationMessage
|
||||
CmdStopNotifyingPruningPointUTXOSetOverrideRequestMessage
|
||||
CmdStopNotifyingPruningPointUTXOSetOverrideResponseMessage
|
||||
CmdEstimateNetworkHashesPerSecondRequestMessage
|
||||
CmdEstimateNetworkHashesPerSecondResponseMessage
|
||||
CmdNotifyVirtualDaaScoreChangedRequestMessage
|
||||
CmdNotifyVirtualDaaScoreChangedResponseMessage
|
||||
CmdVirtualDaaScoreChangedNotificationMessage
|
||||
)
|
||||
|
||||
// ProtocolMessageCommandToString maps all MessageCommands to their string representation
|
||||
@@ -236,8 +246,18 @@ var RPCMessageCommandToString = map[MessageCommand]string{
|
||||
CmdBanResponseMessage: "BanResponse",
|
||||
CmdUnbanRequestMessage: "UnbanRequest",
|
||||
CmdUnbanResponseMessage: "UnbanResponse",
|
||||
CmdGetInfoRequestMessage: "GetInfoRequestMessage",
|
||||
CmdGetInfoResponseMessage: "GeInfoResponseMessage",
|
||||
CmdGetInfoRequestMessage: "GetInfoRequest",
|
||||
CmdGetInfoResponseMessage: "GeInfoResponse",
|
||||
CmdNotifyPruningPointUTXOSetOverrideRequestMessage: "NotifyPruningPointUTXOSetOverrideRequest",
|
||||
CmdNotifyPruningPointUTXOSetOverrideResponseMessage: "NotifyPruningPointUTXOSetOverrideResponse",
|
||||
CmdPruningPointUTXOSetOverrideNotificationMessage: "PruningPointUTXOSetOverrideNotification",
|
||||
CmdStopNotifyingPruningPointUTXOSetOverrideRequestMessage: "StopNotifyingPruningPointUTXOSetOverrideRequest",
|
||||
CmdStopNotifyingPruningPointUTXOSetOverrideResponseMessage: "StopNotifyingPruningPointUTXOSetOverrideResponse",
|
||||
CmdEstimateNetworkHashesPerSecondRequestMessage: "EstimateNetworkHashesPerSecondRequest",
|
||||
CmdEstimateNetworkHashesPerSecondResponseMessage: "EstimateNetworkHashesPerSecondResponse",
|
||||
CmdNotifyVirtualDaaScoreChangedRequestMessage: "NotifyVirtualDaaScoreChangedRequest",
|
||||
CmdNotifyVirtualDaaScoreChangedResponseMessage: "NotifyVirtualDaaScoreChangedResponse",
|
||||
CmdVirtualDaaScoreChangedNotificationMessage: "VirtualDaaScoreChangedNotification",
|
||||
}
|
||||
|
||||
// Message is an interface that describes a kaspa message. A type that
|
||||
|
||||
@@ -15,19 +15,6 @@ import (
|
||||
// backing array multiple times.
|
||||
const defaultTransactionAlloc = 2048
|
||||
|
||||
// MaxMassAcceptedByBlock is the maximum total transaction mass a block may accept.
|
||||
const MaxMassAcceptedByBlock = 10000000
|
||||
|
||||
// MaxMassPerTx is the maximum total mass a transaction may have.
|
||||
const MaxMassPerTx = MaxMassAcceptedByBlock / 2
|
||||
|
||||
// MaxTxPerBlock is the maximum number of transactions that could
|
||||
// possibly fit into a block.
|
||||
const MaxTxPerBlock = (MaxMassAcceptedByBlock / minTxPayload) + 1
|
||||
|
||||
// MaxBlockParents is the maximum allowed number of parents for block.
|
||||
const MaxBlockParents = 10
|
||||
|
||||
// TxLoc holds locator data for the offset and length of where a transaction is
|
||||
// located within a MsgBlock data buffer.
|
||||
type TxLoc struct {
|
||||
|
||||
@@ -31,6 +31,6 @@ type OutpointAndUTXOEntryPair struct {
|
||||
type UTXOEntry struct {
|
||||
Amount uint64
|
||||
ScriptPublicKey *externalapi.ScriptPublicKey
|
||||
BlockBlueScore uint64
|
||||
BlockDAAScore uint64
|
||||
IsCoinbase bool
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ package appmessage
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
||||
"strconv"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
@@ -133,7 +132,6 @@ type MsgTx struct {
|
||||
LockTime uint64
|
||||
SubnetworkID externalapi.DomainSubnetworkID
|
||||
Gas uint64
|
||||
PayloadHash externalapi.DomainHash
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
@@ -179,7 +177,6 @@ func (msg *MsgTx) Copy() *MsgTx {
|
||||
LockTime: msg.LockTime,
|
||||
SubnetworkID: msg.SubnetworkID,
|
||||
Gas: msg.Gas,
|
||||
PayloadHash: msg.PayloadHash,
|
||||
}
|
||||
|
||||
if msg.Payload != nil {
|
||||
@@ -280,18 +277,12 @@ func newMsgTx(version uint16, txIn []*TxIn, txOut []*TxOut, subnetworkID *extern
|
||||
txOut = make([]*TxOut, 0, defaultTxInOutAlloc)
|
||||
}
|
||||
|
||||
var payloadHash externalapi.DomainHash
|
||||
if *subnetworkID != subnetworks.SubnetworkIDNative {
|
||||
payloadHash = *hashes.PayloadHash(payload)
|
||||
}
|
||||
|
||||
return &MsgTx{
|
||||
Version: version,
|
||||
TxIn: txIn,
|
||||
TxOut: txOut,
|
||||
SubnetworkID: *subnetworkID,
|
||||
Gas: gas,
|
||||
PayloadHash: payloadHash,
|
||||
Payload: payload,
|
||||
LockTime: lockTime,
|
||||
}
|
||||
|
||||
@@ -133,8 +133,8 @@ func TestTx(t *testing.T) {
|
||||
|
||||
// TestTxHash tests the ability to generate the hash of a transaction accurately.
|
||||
func TestTxHashAndID(t *testing.T) {
|
||||
txHash1Str := "4bee9ee495bd93a755de428376bd582a2bb6ec37c041753b711c0606d5745c13"
|
||||
txID1Str := "f868bd20e816256b80eac976821be4589d24d21141bd1cec6e8005d0c16c6881"
|
||||
txHash1Str := "93663e597f6c968d32d229002f76408edf30d6a0151ff679fc729812d8cb2acc"
|
||||
txID1Str := "24079c6d2bdf602fc389cc307349054937744a9c8dc0f07c023e6af0e949a4e7"
|
||||
wantTxID1, err := transactionid.FromString(txID1Str)
|
||||
if err != nil {
|
||||
t.Fatalf("NewTxIDFromStr: %v", err)
|
||||
@@ -185,14 +185,14 @@ func TestTxHashAndID(t *testing.T) {
|
||||
spew.Sprint(tx1ID), spew.Sprint(wantTxID1))
|
||||
}
|
||||
|
||||
hash2Str := "cb1bdb4a83d4885535fb3cceb5c96597b7df903db83f0ffcd779d703affd8efd"
|
||||
hash2Str := "8dafd1bec24527d8e3b443ceb0a3b92fffc0d60026317f890b2faf5e9afc177a"
|
||||
wantHash2, err := externalapi.NewDomainHashFromString(hash2Str)
|
||||
if err != nil {
|
||||
t.Errorf("NewTxIDFromStr: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
id2Str := "ca080073d4ddf5b84443a0964af633f3c70a5b290fd3bc35a7e6f93fd33f9330"
|
||||
id2Str := "89ffb49474637502d9059af38b8a95fc2f0d3baef5c801d7a9b9c8830671b711"
|
||||
wantID2, err := transactionid.FromString(id2Str)
|
||||
if err != nil {
|
||||
t.Errorf("NewTxIDFromStr: %v", err)
|
||||
|
||||
@@ -19,7 +19,7 @@ func TestVersion(t *testing.T) {
|
||||
|
||||
// Create version message data.
|
||||
tcpAddrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 16111}
|
||||
me := NewNetAddress(tcpAddrMe, SFNodeNetwork)
|
||||
me := NewNetAddress(tcpAddrMe)
|
||||
generatedID, err := id.GenerateID()
|
||||
if err != nil {
|
||||
t.Fatalf("id.GenerateID: %s", err)
|
||||
|
||||
@@ -15,9 +15,6 @@ type NetAddress struct {
|
||||
// Last time the address was seen.
|
||||
Timestamp mstime.Time
|
||||
|
||||
// Bitfield which identifies the services supported by the address.
|
||||
Services ServiceFlag
|
||||
|
||||
// IP address of the peer.
|
||||
IP net.IP
|
||||
|
||||
@@ -26,17 +23,6 @@ type NetAddress struct {
|
||||
Port uint16
|
||||
}
|
||||
|
||||
// HasService returns whether the specified service is supported by the address.
|
||||
func (na *NetAddress) HasService(service ServiceFlag) bool {
|
||||
return na.Services&service == service
|
||||
}
|
||||
|
||||
// AddService adds service as a supported service by the peer generating the
|
||||
// message.
|
||||
func (na *NetAddress) AddService(service ServiceFlag) {
|
||||
na.Services |= service
|
||||
}
|
||||
|
||||
// TCPAddress converts the NetAddress to *net.TCPAddr
|
||||
func (na *NetAddress) TCPAddress() *net.TCPAddr {
|
||||
return &net.TCPAddr{
|
||||
@@ -47,20 +33,19 @@ func (na *NetAddress) TCPAddress() *net.TCPAddr {
|
||||
|
||||
// NewNetAddressIPPort returns a new NetAddress using the provided IP, port, and
|
||||
// supported services with defaults for the remaining fields.
|
||||
func NewNetAddressIPPort(ip net.IP, port uint16, services ServiceFlag) *NetAddress {
|
||||
return NewNetAddressTimestamp(mstime.Now(), services, ip, port)
|
||||
func NewNetAddressIPPort(ip net.IP, port uint16) *NetAddress {
|
||||
return NewNetAddressTimestamp(mstime.Now(), ip, port)
|
||||
}
|
||||
|
||||
// NewNetAddressTimestamp returns a new NetAddress using the provided
|
||||
// timestamp, IP, port, and supported services. The timestamp is rounded to
|
||||
// single millisecond precision.
|
||||
func NewNetAddressTimestamp(
|
||||
timestamp mstime.Time, services ServiceFlag, ip net.IP, port uint16) *NetAddress {
|
||||
timestamp mstime.Time, ip net.IP, port uint16) *NetAddress {
|
||||
// Limit the timestamp to one millisecond precision since the protocol
|
||||
// doesn't support better.
|
||||
na := NetAddress{
|
||||
Timestamp: timestamp,
|
||||
Services: services,
|
||||
IP: ip,
|
||||
Port: port,
|
||||
}
|
||||
@@ -69,6 +54,6 @@ func NewNetAddressTimestamp(
|
||||
|
||||
// NewNetAddress returns a new NetAddress using the provided TCP address and
|
||||
// supported services with defaults for the remaining fields.
|
||||
func NewNetAddress(addr *net.TCPAddr, services ServiceFlag) *NetAddress {
|
||||
return NewNetAddressIPPort(addr.IP, uint16(addr.Port), services)
|
||||
func NewNetAddress(addr *net.TCPAddr) *NetAddress {
|
||||
return NewNetAddressIPPort(addr.IP, uint16(addr.Port))
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ func TestNetAddress(t *testing.T) {
|
||||
port := 16111
|
||||
|
||||
// Test NewNetAddress.
|
||||
na := NewNetAddress(&net.TCPAddr{IP: ip, Port: port}, 0)
|
||||
na := NewNetAddress(&net.TCPAddr{IP: ip, Port: port})
|
||||
|
||||
// Ensure we get the same ip, port, and services back out.
|
||||
if !na.IP.Equal(ip) {
|
||||
@@ -25,21 +25,4 @@ func TestNetAddress(t *testing.T) {
|
||||
t.Errorf("NetNetAddress: wrong port - got %v, want %v", na.Port,
|
||||
port)
|
||||
}
|
||||
if na.Services != 0 {
|
||||
t.Errorf("NetNetAddress: wrong services - got %v, want %v",
|
||||
na.Services, 0)
|
||||
}
|
||||
if na.HasService(SFNodeNetwork) {
|
||||
t.Errorf("HasService: SFNodeNetwork service is set")
|
||||
}
|
||||
|
||||
// Ensure adding the full service node flag works.
|
||||
na.AddService(SFNodeNetwork)
|
||||
if na.Services != SFNodeNetwork {
|
||||
t.Errorf("AddService: wrong services - got %v, want %v",
|
||||
na.Services, SFNodeNetwork)
|
||||
}
|
||||
if !na.HasService(SFNodeNetwork) {
|
||||
t.Errorf("HasService: SFNodeNetwork service not set")
|
||||
}
|
||||
}
|
||||
|
||||
43
app/appmessage/rpc_estimate_network_hashes_per_second.go
Normal file
43
app/appmessage/rpc_estimate_network_hashes_per_second.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package appmessage
|
||||
|
||||
// EstimateNetworkHashesPerSecondRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type EstimateNetworkHashesPerSecondRequestMessage struct {
|
||||
baseMessage
|
||||
StartHash string
|
||||
WindowSize uint32
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *EstimateNetworkHashesPerSecondRequestMessage) Command() MessageCommand {
|
||||
return CmdEstimateNetworkHashesPerSecondRequestMessage
|
||||
}
|
||||
|
||||
// NewEstimateNetworkHashesPerSecondRequestMessage returns a instance of the message
|
||||
func NewEstimateNetworkHashesPerSecondRequestMessage(startHash string, windowSize uint32) *EstimateNetworkHashesPerSecondRequestMessage {
|
||||
return &EstimateNetworkHashesPerSecondRequestMessage{
|
||||
StartHash: startHash,
|
||||
WindowSize: windowSize,
|
||||
}
|
||||
}
|
||||
|
||||
// EstimateNetworkHashesPerSecondResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type EstimateNetworkHashesPerSecondResponseMessage struct {
|
||||
baseMessage
|
||||
NetworkHashesPerSecond uint64
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *EstimateNetworkHashesPerSecondResponseMessage) Command() MessageCommand {
|
||||
return CmdEstimateNetworkHashesPerSecondResponseMessage
|
||||
}
|
||||
|
||||
// NewEstimateNetworkHashesPerSecondResponseMessage returns a instance of the message
|
||||
func NewEstimateNetworkHashesPerSecondResponseMessage(networkHashesPerSecond uint64) *EstimateNetworkHashesPerSecondResponseMessage {
|
||||
return &EstimateNetworkHashesPerSecondResponseMessage{
|
||||
NetworkHashesPerSecond: networkHashesPerSecond,
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@ package appmessage
|
||||
// its respective RPC message
|
||||
type GetBlockRequestMessage struct {
|
||||
baseMessage
|
||||
Hash string
|
||||
IncludeTransactionVerboseData bool
|
||||
Hash string
|
||||
IncludeTransactions bool
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
@@ -14,10 +14,10 @@ func (msg *GetBlockRequestMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewGetBlockRequestMessage returns a instance of the message
|
||||
func NewGetBlockRequestMessage(hash string, includeTransactionVerboseData bool) *GetBlockRequestMessage {
|
||||
func NewGetBlockRequestMessage(hash string, includeTransactions bool) *GetBlockRequestMessage {
|
||||
return &GetBlockRequestMessage{
|
||||
Hash: hash,
|
||||
IncludeTransactionVerboseData: includeTransactionVerboseData,
|
||||
Hash: hash,
|
||||
IncludeTransactions: includeTransactions,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ func NewGetBlockRequestMessage(hash string, includeTransactionVerboseData bool)
|
||||
// its respective RPC message
|
||||
type GetBlockResponseMessage struct {
|
||||
baseMessage
|
||||
BlockVerboseData *BlockVerboseData
|
||||
Block *RPCBlock
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
@@ -39,69 +39,3 @@ func (msg *GetBlockResponseMessage) Command() MessageCommand {
|
||||
func NewGetBlockResponseMessage() *GetBlockResponseMessage {
|
||||
return &GetBlockResponseMessage{}
|
||||
}
|
||||
|
||||
// BlockVerboseData holds verbose data about a block
|
||||
type BlockVerboseData struct {
|
||||
Hash string
|
||||
Version uint16
|
||||
VersionHex string
|
||||
HashMerkleRoot string
|
||||
AcceptedIDMerkleRoot string
|
||||
UTXOCommitment string
|
||||
TxIDs []string
|
||||
TransactionVerboseData []*TransactionVerboseData
|
||||
Time int64
|
||||
Nonce uint64
|
||||
Bits string
|
||||
Difficulty float64
|
||||
ParentHashes []string
|
||||
SelectedParentHash string
|
||||
BlueScore uint64
|
||||
IsHeaderOnly bool
|
||||
}
|
||||
|
||||
// TransactionVerboseData holds verbose data about a transaction
|
||||
type TransactionVerboseData struct {
|
||||
TxID string
|
||||
Hash string
|
||||
Size uint64
|
||||
Version uint16
|
||||
LockTime uint64
|
||||
SubnetworkID string
|
||||
Gas uint64
|
||||
PayloadHash string
|
||||
Payload string
|
||||
TransactionVerboseInputs []*TransactionVerboseInput
|
||||
TransactionVerboseOutputs []*TransactionVerboseOutput
|
||||
BlockHash string
|
||||
Time uint64
|
||||
BlockTime uint64
|
||||
}
|
||||
|
||||
// TransactionVerboseInput holds data about a transaction input
|
||||
type TransactionVerboseInput struct {
|
||||
TxID string
|
||||
OutputIndex uint32
|
||||
ScriptSig *ScriptSig
|
||||
Sequence uint64
|
||||
}
|
||||
|
||||
// ScriptSig holds data about a script signature
|
||||
type ScriptSig struct {
|
||||
Asm string
|
||||
Hex string
|
||||
}
|
||||
|
||||
// TransactionVerboseOutput holds data about a transaction output
|
||||
type TransactionVerboseOutput struct {
|
||||
Value uint64
|
||||
Index uint32
|
||||
ScriptPubKey *ScriptPubKeyResult
|
||||
}
|
||||
|
||||
// ScriptPubKeyResult holds data about a script public key
|
||||
type ScriptPubKeyResult struct {
|
||||
Hex string
|
||||
Type string
|
||||
Address string
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ type GetBlockDAGInfoResponseMessage struct {
|
||||
VirtualParentHashes []string
|
||||
Difficulty float64
|
||||
PastMedianTime int64
|
||||
PruningPointHash string
|
||||
VirtualDAAScore uint64
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ func NewGetBlockTemplateRequestMessage(payAddress string) *GetBlockTemplateReque
|
||||
// its respective RPC message
|
||||
type GetBlockTemplateResponseMessage struct {
|
||||
baseMessage
|
||||
MsgBlock *MsgBlock
|
||||
Block *RPCBlock
|
||||
IsSynced bool
|
||||
|
||||
Error *RPCError
|
||||
@@ -35,9 +35,9 @@ func (msg *GetBlockTemplateResponseMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewGetBlockTemplateResponseMessage returns a instance of the message
|
||||
func NewGetBlockTemplateResponseMessage(msgBlock *MsgBlock, isSynced bool) *GetBlockTemplateResponseMessage {
|
||||
func NewGetBlockTemplateResponseMessage(block *RPCBlock, isSynced bool) *GetBlockTemplateResponseMessage {
|
||||
return &GetBlockTemplateResponseMessage{
|
||||
MsgBlock: msgBlock,
|
||||
Block: block,
|
||||
IsSynced: isSynced,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ package appmessage
|
||||
// its respective RPC message
|
||||
type GetBlocksRequestMessage struct {
|
||||
baseMessage
|
||||
LowHash string
|
||||
IncludeBlockVerboseData bool
|
||||
IncludeTransactionVerboseData bool
|
||||
LowHash string
|
||||
IncludeBlocks bool
|
||||
IncludeTransactions bool
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
@@ -15,12 +15,12 @@ func (msg *GetBlocksRequestMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewGetBlocksRequestMessage returns a instance of the message
|
||||
func NewGetBlocksRequestMessage(lowHash string, includeBlockVerboseData bool,
|
||||
includeTransactionVerboseData bool) *GetBlocksRequestMessage {
|
||||
func NewGetBlocksRequestMessage(lowHash string, includeBlocks bool,
|
||||
includeTransactions bool) *GetBlocksRequestMessage {
|
||||
return &GetBlocksRequestMessage{
|
||||
LowHash: lowHash,
|
||||
IncludeBlockVerboseData: includeBlockVerboseData,
|
||||
IncludeTransactionVerboseData: includeTransactionVerboseData,
|
||||
LowHash: lowHash,
|
||||
IncludeBlocks: includeBlocks,
|
||||
IncludeTransactions: includeTransactions,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ func NewGetBlocksRequestMessage(lowHash string, includeBlockVerboseData bool,
|
||||
// its respective RPC message
|
||||
type GetBlocksResponseMessage struct {
|
||||
baseMessage
|
||||
BlockHashes []string
|
||||
BlockVerboseData []*BlockVerboseData
|
||||
BlockHashes []string
|
||||
Blocks []*RPCBlock
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
@@ -40,11 +40,6 @@ func (msg *GetBlocksResponseMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewGetBlocksResponseMessage returns a instance of the message
|
||||
func NewGetBlocksResponseMessage(blockHashes []string, blockHexes []string,
|
||||
blockVerboseData []*BlockVerboseData) *GetBlocksResponseMessage {
|
||||
|
||||
return &GetBlocksResponseMessage{
|
||||
BlockHashes: blockHashes,
|
||||
BlockVerboseData: blockVerboseData,
|
||||
}
|
||||
func NewGetBlocksResponseMessage() *GetBlocksResponseMessage {
|
||||
return &GetBlocksResponseMessage{}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ func (msg *GetInfoRequestMessage) Command() MessageCommand {
|
||||
return CmdGetInfoRequestMessage
|
||||
}
|
||||
|
||||
// NewGeInfoRequestMessage returns a instance of the message
|
||||
func NewGeInfoRequestMessage() *GetInfoRequestMessage {
|
||||
// NewGetInfoRequestMessage returns a instance of the message
|
||||
func NewGetInfoRequestMessage() *GetInfoRequestMessage {
|
||||
return &GetInfoRequestMessage{}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ func NewGeInfoRequestMessage() *GetInfoRequestMessage {
|
||||
// its respective RPC message
|
||||
type GetInfoResponseMessage struct {
|
||||
baseMessage
|
||||
P2PID string
|
||||
P2PID string
|
||||
MempoolSize uint64
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
@@ -31,8 +32,9 @@ func (msg *GetInfoResponseMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewGetInfoResponseMessage returns a instance of the message
|
||||
func NewGetInfoResponseMessage(p2pID string) *GetInfoResponseMessage {
|
||||
func NewGetInfoResponseMessage(p2pID string, mempoolSize uint64) *GetInfoResponseMessage {
|
||||
return &GetInfoResponseMessage{
|
||||
P2PID: p2pID,
|
||||
P2PID: p2pID,
|
||||
MempoolSize: mempoolSize,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ type GetMempoolEntryResponseMessage struct {
|
||||
|
||||
// MempoolEntry represents a transaction in the mempool.
|
||||
type MempoolEntry struct {
|
||||
Fee uint64
|
||||
TransactionVerboseData *TransactionVerboseData
|
||||
Fee uint64
|
||||
Transaction *RPCTransaction
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
@@ -38,11 +38,11 @@ func (msg *GetMempoolEntryResponseMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewGetMempoolEntryResponseMessage returns a instance of the message
|
||||
func NewGetMempoolEntryResponseMessage(fee uint64, transactionVerboseData *TransactionVerboseData) *GetMempoolEntryResponseMessage {
|
||||
func NewGetMempoolEntryResponseMessage(fee uint64, transaction *RPCTransaction) *GetMempoolEntryResponseMessage {
|
||||
return &GetMempoolEntryResponseMessage{
|
||||
Entry: &MempoolEntry{
|
||||
Fee: fee,
|
||||
TransactionVerboseData: transactionVerboseData,
|
||||
Fee: fee,
|
||||
Transaction: transaction,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,7 @@ func NewNotifyBlockAddedResponseMessage() *NotifyBlockAddedResponseMessage {
|
||||
// its respective RPC message
|
||||
type BlockAddedNotificationMessage struct {
|
||||
baseMessage
|
||||
Block *MsgBlock
|
||||
BlockVerboseData *BlockVerboseData
|
||||
Block *RPCBlock
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
@@ -47,9 +46,8 @@ func (msg *BlockAddedNotificationMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewBlockAddedNotificationMessage returns a instance of the message
|
||||
func NewBlockAddedNotificationMessage(block *MsgBlock, blockVerboseData *BlockVerboseData) *BlockAddedNotificationMessage {
|
||||
func NewBlockAddedNotificationMessage(block *RPCBlock) *BlockAddedNotificationMessage {
|
||||
return &BlockAddedNotificationMessage{
|
||||
Block: block,
|
||||
BlockVerboseData: blockVerboseData,
|
||||
Block: block,
|
||||
}
|
||||
}
|
||||
|
||||
83
app/appmessage/rpc_notify_pruning_point_utxo_set_override.go
Normal file
83
app/appmessage/rpc_notify_pruning_point_utxo_set_override.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package appmessage
|
||||
|
||||
// NotifyPruningPointUTXOSetOverrideRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type NotifyPruningPointUTXOSetOverrideRequestMessage struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *NotifyPruningPointUTXOSetOverrideRequestMessage) Command() MessageCommand {
|
||||
return CmdNotifyPruningPointUTXOSetOverrideRequestMessage
|
||||
}
|
||||
|
||||
// NewNotifyPruningPointUTXOSetOverrideRequestMessage returns a instance of the message
|
||||
func NewNotifyPruningPointUTXOSetOverrideRequestMessage() *NotifyPruningPointUTXOSetOverrideRequestMessage {
|
||||
return &NotifyPruningPointUTXOSetOverrideRequestMessage{}
|
||||
}
|
||||
|
||||
// NotifyPruningPointUTXOSetOverrideResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type NotifyPruningPointUTXOSetOverrideResponseMessage struct {
|
||||
baseMessage
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *NotifyPruningPointUTXOSetOverrideResponseMessage) Command() MessageCommand {
|
||||
return CmdNotifyPruningPointUTXOSetOverrideResponseMessage
|
||||
}
|
||||
|
||||
// NewNotifyPruningPointUTXOSetOverrideResponseMessage returns a instance of the message
|
||||
func NewNotifyPruningPointUTXOSetOverrideResponseMessage() *NotifyPruningPointUTXOSetOverrideResponseMessage {
|
||||
return &NotifyPruningPointUTXOSetOverrideResponseMessage{}
|
||||
}
|
||||
|
||||
// PruningPointUTXOSetOverrideNotificationMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type PruningPointUTXOSetOverrideNotificationMessage struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *PruningPointUTXOSetOverrideNotificationMessage) Command() MessageCommand {
|
||||
return CmdPruningPointUTXOSetOverrideNotificationMessage
|
||||
}
|
||||
|
||||
// NewPruningPointUTXOSetOverrideNotificationMessage returns a instance of the message
|
||||
func NewPruningPointUTXOSetOverrideNotificationMessage() *PruningPointUTXOSetOverrideNotificationMessage {
|
||||
return &PruningPointUTXOSetOverrideNotificationMessage{}
|
||||
}
|
||||
|
||||
// StopNotifyingPruningPointUTXOSetOverrideRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type StopNotifyingPruningPointUTXOSetOverrideRequestMessage struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *StopNotifyingPruningPointUTXOSetOverrideRequestMessage) Command() MessageCommand {
|
||||
return CmdNotifyPruningPointUTXOSetOverrideRequestMessage
|
||||
}
|
||||
|
||||
// NewStopNotifyingPruningPointUTXOSetOverrideRequestMessage returns a instance of the message
|
||||
func NewStopNotifyingPruningPointUTXOSetOverrideRequestMessage() *StopNotifyingPruningPointUTXOSetOverrideRequestMessage {
|
||||
return &StopNotifyingPruningPointUTXOSetOverrideRequestMessage{}
|
||||
}
|
||||
|
||||
// StopNotifyingPruningPointUTXOSetOverrideResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type StopNotifyingPruningPointUTXOSetOverrideResponseMessage struct {
|
||||
baseMessage
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *StopNotifyingPruningPointUTXOSetOverrideResponseMessage) Command() MessageCommand {
|
||||
return CmdNotifyPruningPointUTXOSetOverrideResponseMessage
|
||||
}
|
||||
|
||||
// NewStopNotifyingPruningPointUTXOSetOverrideResponseMessage returns a instance of the message
|
||||
func NewStopNotifyingPruningPointUTXOSetOverrideResponseMessage() *StopNotifyingPruningPointUTXOSetOverrideResponseMessage {
|
||||
return &StopNotifyingPruningPointUTXOSetOverrideResponseMessage{}
|
||||
}
|
||||
55
app/appmessage/rpc_notify_virtual_daa_score_changed.go
Normal file
55
app/appmessage/rpc_notify_virtual_daa_score_changed.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package appmessage
|
||||
|
||||
// NotifyVirtualDaaScoreChangedRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type NotifyVirtualDaaScoreChangedRequestMessage struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *NotifyVirtualDaaScoreChangedRequestMessage) Command() MessageCommand {
|
||||
return CmdNotifyVirtualDaaScoreChangedRequestMessage
|
||||
}
|
||||
|
||||
// NewNotifyVirtualDaaScoreChangedRequestMessage returns a instance of the message
|
||||
func NewNotifyVirtualDaaScoreChangedRequestMessage() *NotifyVirtualDaaScoreChangedRequestMessage {
|
||||
return &NotifyVirtualDaaScoreChangedRequestMessage{}
|
||||
}
|
||||
|
||||
// NotifyVirtualDaaScoreChangedResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type NotifyVirtualDaaScoreChangedResponseMessage struct {
|
||||
baseMessage
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *NotifyVirtualDaaScoreChangedResponseMessage) Command() MessageCommand {
|
||||
return CmdNotifyVirtualDaaScoreChangedResponseMessage
|
||||
}
|
||||
|
||||
// NewNotifyVirtualDaaScoreChangedResponseMessage returns a instance of the message
|
||||
func NewNotifyVirtualDaaScoreChangedResponseMessage() *NotifyVirtualDaaScoreChangedResponseMessage {
|
||||
return &NotifyVirtualDaaScoreChangedResponseMessage{}
|
||||
}
|
||||
|
||||
// VirtualDaaScoreChangedNotificationMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type VirtualDaaScoreChangedNotificationMessage struct {
|
||||
baseMessage
|
||||
VirtualDaaScore uint64
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *VirtualDaaScoreChangedNotificationMessage) Command() MessageCommand {
|
||||
return CmdVirtualDaaScoreChangedNotificationMessage
|
||||
}
|
||||
|
||||
// NewVirtualDaaScoreChangedNotificationMessage returns a instance of the message
|
||||
func NewVirtualDaaScoreChangedNotificationMessage(
|
||||
virtualDaaScore uint64) *VirtualDaaScoreChangedNotificationMessage {
|
||||
|
||||
return &VirtualDaaScoreChangedNotificationMessage{
|
||||
VirtualDaaScore: virtualDaaScore,
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ package appmessage
|
||||
// its respective RPC message
|
||||
type SubmitBlockRequestMessage struct {
|
||||
baseMessage
|
||||
Block *MsgBlock
|
||||
Block *RPCBlock
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
@@ -13,7 +13,7 @@ func (msg *SubmitBlockRequestMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewSubmitBlockRequestMessage returns a instance of the message
|
||||
func NewSubmitBlockRequestMessage(block *MsgBlock) *SubmitBlockRequestMessage {
|
||||
func NewSubmitBlockRequestMessage(block *RPCBlock) *SubmitBlockRequestMessage {
|
||||
return &SubmitBlockRequestMessage{
|
||||
Block: block,
|
||||
}
|
||||
@@ -57,3 +57,35 @@ func (msg *SubmitBlockResponseMessage) Command() MessageCommand {
|
||||
func NewSubmitBlockResponseMessage() *SubmitBlockResponseMessage {
|
||||
return &SubmitBlockResponseMessage{}
|
||||
}
|
||||
|
||||
// RPCBlock is a kaspad block representation meant to be
|
||||
// used over RPC
|
||||
type RPCBlock struct {
|
||||
Header *RPCBlockHeader
|
||||
Transactions []*RPCTransaction
|
||||
VerboseData *RPCBlockVerboseData
|
||||
}
|
||||
|
||||
// RPCBlockHeader is a kaspad block header representation meant to be
|
||||
// used over RPC
|
||||
type RPCBlockHeader struct {
|
||||
Version uint32
|
||||
ParentHashes []string
|
||||
HashMerkleRoot string
|
||||
AcceptedIDMerkleRoot string
|
||||
UTXOCommitment string
|
||||
Timestamp int64
|
||||
Bits uint32
|
||||
Nonce uint64
|
||||
}
|
||||
|
||||
// RPCBlockVerboseData holds verbose data about a block
|
||||
type RPCBlockVerboseData struct {
|
||||
Hash string
|
||||
Difficulty float64
|
||||
SelectedParentHash string
|
||||
TransactionIDs []string
|
||||
IsHeaderOnly bool
|
||||
BlueScore uint64
|
||||
ChildrenHashes []string
|
||||
}
|
||||
|
||||
@@ -49,8 +49,8 @@ type RPCTransaction struct {
|
||||
LockTime uint64
|
||||
SubnetworkID string
|
||||
Gas uint64
|
||||
PayloadHash string
|
||||
Payload string
|
||||
VerboseData *RPCTransactionVerboseData
|
||||
}
|
||||
|
||||
// RPCTransactionInput is a kaspad transaction input representation
|
||||
@@ -59,6 +59,7 @@ type RPCTransactionInput struct {
|
||||
PreviousOutpoint *RPCOutpoint
|
||||
SignatureScript string
|
||||
Sequence uint64
|
||||
VerboseData *RPCTransactionInputVerboseData
|
||||
}
|
||||
|
||||
// RPCScriptPublicKey is a kaspad ScriptPublicKey representation
|
||||
@@ -72,6 +73,7 @@ type RPCScriptPublicKey struct {
|
||||
type RPCTransactionOutput struct {
|
||||
Amount uint64
|
||||
ScriptPublicKey *RPCScriptPublicKey
|
||||
VerboseData *RPCTransactionOutputVerboseData
|
||||
}
|
||||
|
||||
// RPCOutpoint is a kaspad outpoint representation meant to be used
|
||||
@@ -86,6 +88,25 @@ type RPCOutpoint struct {
|
||||
type RPCUTXOEntry struct {
|
||||
Amount uint64
|
||||
ScriptPublicKey *RPCScriptPublicKey
|
||||
BlockBlueScore uint64
|
||||
BlockDAAScore uint64
|
||||
IsCoinbase bool
|
||||
}
|
||||
|
||||
// RPCTransactionVerboseData holds verbose data about a transaction
|
||||
type RPCTransactionVerboseData struct {
|
||||
TransactionID string
|
||||
Hash string
|
||||
Size uint64
|
||||
BlockHash string
|
||||
BlockTime uint64
|
||||
}
|
||||
|
||||
// RPCTransactionInputVerboseData holds data about a transaction input
|
||||
type RPCTransactionInputVerboseData struct {
|
||||
}
|
||||
|
||||
// RPCTransactionOutputVerboseData holds data about a transaction output
|
||||
type RPCTransactionOutputVerboseData struct {
|
||||
ScriptPublicKeyType string
|
||||
ScriptPublicKeyAddress string
|
||||
}
|
||||
|
||||
@@ -4,23 +4,17 @@ import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/utxoindex"
|
||||
|
||||
infrastructuredatabase "github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
|
||||
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/id"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/protocol"
|
||||
"github.com/kaspanet/kaspad/app/rpc"
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
"github.com/kaspanet/kaspad/domain/utxoindex"
|
||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||
infrastructuredatabase "github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/connmanager"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/dnsseed"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/id"
|
||||
"github.com/kaspanet/kaspad/util/panics"
|
||||
)
|
||||
|
||||
@@ -50,8 +44,6 @@ func (a *ComponentManager) Start() {
|
||||
panics.Exit(log, fmt.Sprintf("Error starting the net adapter: %+v", err))
|
||||
}
|
||||
|
||||
a.maybeSeedFromDNS()
|
||||
|
||||
a.connectionManager.Start()
|
||||
}
|
||||
|
||||
@@ -72,6 +64,8 @@ func (a *ComponentManager) Stop() {
|
||||
log.Errorf("Error stopping the net adapter: %+v", err)
|
||||
}
|
||||
|
||||
a.protocolManager.Close()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -80,7 +74,13 @@ func (a *ComponentManager) Stop() {
|
||||
func NewComponentManager(cfg *config.Config, db infrastructuredatabase.Database, interrupt chan<- struct{}) (
|
||||
*ComponentManager, error) {
|
||||
|
||||
domain, err := domain.New(cfg.ActiveNetParams, db, cfg.IsArchivalNode)
|
||||
consensusConfig := consensus.Config{
|
||||
Params: *cfg.ActiveNetParams,
|
||||
IsArchival: cfg.IsArchivalNode,
|
||||
EnableSanityCheckPruningUTXOSet: cfg.EnableSanityCheckPruningUTXOSet,
|
||||
}
|
||||
|
||||
domain, err := domain.New(&consensusConfig, db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -97,7 +97,11 @@ func NewComponentManager(cfg *config.Config, db infrastructuredatabase.Database,
|
||||
|
||||
var utxoIndex *utxoindex.UTXOIndex
|
||||
if cfg.UTXOIndex {
|
||||
utxoIndex = utxoindex.New(domain.Consensus(), db)
|
||||
utxoIndex, err = utxoindex.New(domain.Consensus(), db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Infof("UTXO index started")
|
||||
}
|
||||
|
||||
@@ -144,27 +148,11 @@ func setupRPC(
|
||||
shutDownChan,
|
||||
)
|
||||
protocolManager.SetOnBlockAddedToDAGHandler(rpcManager.NotifyBlockAddedToDAG)
|
||||
protocolManager.SetOnPruningPointUTXOSetOverrideHandler(rpcManager.NotifyPruningPointUTXOSetOverride)
|
||||
|
||||
return rpcManager
|
||||
}
|
||||
|
||||
func (a *ComponentManager) maybeSeedFromDNS() {
|
||||
if !a.cfg.DisableDNSSeed {
|
||||
dnsseed.SeedFromDNS(a.cfg.NetParams(), a.cfg.DNSSeed, appmessage.SFNodeNetwork, false, nil,
|
||||
a.cfg.Lookup, func(addresses []*appmessage.NetAddress) {
|
||||
// Kaspad uses a lookup of the dns seeder here. Since seeder returns
|
||||
// IPs of nodes and not its own IP, we can not know real IP of
|
||||
// source. So we'll take first returned address as source.
|
||||
a.addressManager.AddAddresses(addresses...)
|
||||
})
|
||||
|
||||
dnsseed.SeedFromGRPC(a.cfg.NetParams(), a.cfg.GRPCSeed, appmessage.SFNodeNetwork, false, nil,
|
||||
func(addresses []*appmessage.NetAddress) {
|
||||
a.addressManager.AddAddresses(addresses...)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// P2PNodeID returns the network ID associated with this ComponentManager
|
||||
func (a *ComponentManager) P2PNodeID() *id.ID {
|
||||
return a.netAdapter.ID()
|
||||
|
||||
57
app/db_version.go
Normal file
57
app/db_version.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const currentDatabaseVersion = 1
|
||||
|
||||
func checkDatabaseVersion(dbPath string) (err error) {
|
||||
versionFileName := versionFilePath(dbPath)
|
||||
|
||||
versionBytes, err := os.ReadFile(versionFileName)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) { // If version file doesn't exist, we assume that the database is new
|
||||
return createDatabaseVersionFile(dbPath, versionFileName)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
databaseVersion, err := strconv.Atoi(string(versionBytes))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if databaseVersion != currentDatabaseVersion {
|
||||
// TODO: Once there's more then one database version, it might make sense to add upgrade logic at this point
|
||||
return errors.Errorf("Invalid database version %d. Expected version: %d", databaseVersion, currentDatabaseVersion)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createDatabaseVersionFile(dbPath string, versionFileName string) error {
|
||||
err := os.MkdirAll(dbPath, 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
versionFile, err := os.Create(versionFileName)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer versionFile.Close()
|
||||
|
||||
versionString := strconv.Itoa(currentDatabaseVersion)
|
||||
_, err = versionFile.Write([]byte(versionString))
|
||||
return err
|
||||
}
|
||||
|
||||
func versionFilePath(dbPath string) string {
|
||||
dbVersionFileName := path.Join(dbPath, "version")
|
||||
return dbVersionFileName
|
||||
}
|
||||
@@ -7,8 +7,6 @@ package app
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/util/panics"
|
||||
)
|
||||
|
||||
var log, _ = logger.Get(logger.SubsystemTags.KASD)
|
||||
var spawn = panics.GoroutineWrapperFunc(log)
|
||||
var log = logger.RegisterSubSystem("KASD")
|
||||
|
||||
@@ -2,6 +2,7 @@ package flowcontext
|
||||
|
||||
import (
|
||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
@@ -36,12 +37,14 @@ func (f *FlowContext) OnNewBlock(block *externalapi.DomainBlock,
|
||||
newBlockInsertionResults = append(newBlockInsertionResults, unorphaningResult.blockInsertionResult)
|
||||
}
|
||||
|
||||
allAcceptedTransactions := make([]*externalapi.DomainTransaction, 0)
|
||||
for i, newBlock := range newBlocks {
|
||||
log.Debugf("OnNewBlock: passing block %s transactions to mining manager", hash)
|
||||
_, err = f.Domain().MiningManager().HandleNewBlockTransactions(newBlock.Transactions)
|
||||
acceptedTransactions, err := f.Domain().MiningManager().HandleNewBlockTransactions(newBlock.Transactions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
allAcceptedTransactions = append(allAcceptedTransactions, acceptedTransactions...)
|
||||
|
||||
if f.onBlockAddedToDAGHandler != nil {
|
||||
log.Debugf("OnNewBlock: calling f.onBlockAddedToDAGHandler for block %s", hash)
|
||||
@@ -53,13 +56,22 @@ func (f *FlowContext) OnNewBlock(block *externalapi.DomainBlock,
|
||||
}
|
||||
}
|
||||
|
||||
return f.broadcastTransactionsAfterBlockAdded(newBlocks, allAcceptedTransactions)
|
||||
}
|
||||
|
||||
// OnPruningPointUTXOSetOverride calls the handler function whenever the UTXO set
|
||||
// resets due to pruning point change via IBD.
|
||||
func (f *FlowContext) OnPruningPointUTXOSetOverride() error {
|
||||
if f.onPruningPointUTXOSetOverrideHandler != nil {
|
||||
return f.onPruningPointUTXOSetOverrideHandler()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FlowContext) broadcastTransactionsAfterBlockAdded(
|
||||
block *externalapi.DomainBlock, transactionsAcceptedToMempool []*externalapi.DomainTransaction) error {
|
||||
addedBlocks []*externalapi.DomainBlock, transactionsAcceptedToMempool []*externalapi.DomainTransaction) error {
|
||||
|
||||
f.updateTransactionsToRebroadcast(block)
|
||||
f.updateTransactionsToRebroadcast(addedBlocks)
|
||||
|
||||
// Don't relay transactions when in IBD.
|
||||
if f.IsIBDRunning() {
|
||||
@@ -98,6 +110,10 @@ func (f *FlowContext) SharedRequestedBlocks() *blockrelay.SharedRequestedBlocks
|
||||
|
||||
// AddBlock adds the given block to the DAG and propagates it.
|
||||
func (f *FlowContext) AddBlock(block *externalapi.DomainBlock) error {
|
||||
if len(block.Transactions) == 0 {
|
||||
return protocolerrors.Errorf(false, "cannot add header only block")
|
||||
}
|
||||
|
||||
blockInsertionResult, err := f.Domain().Consensus().ValidateAndInsertBlock(block)
|
||||
if err != nil {
|
||||
if errors.As(err, &ruleerrors.RuleError{}) {
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
func (*FlowContext) HandleError(err error, flowName string, isStopping *uint32, errChan chan<- error) {
|
||||
isErrRouteClosed := errors.Is(err, router.ErrRouteClosed)
|
||||
if !isErrRouteClosed {
|
||||
if protocolErr := &(protocolerrors.ProtocolError{}); !errors.As(err, &protocolErr) {
|
||||
if protocolErr := (protocolerrors.ProtocolError{}); !errors.As(err, &protocolErr) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,10 @@ import (
|
||||
// when a block is added to the DAG
|
||||
type OnBlockAddedToDAGHandler func(block *externalapi.DomainBlock, blockInsertionResult *externalapi.BlockInsertionResult) error
|
||||
|
||||
// OnPruningPointUTXOSetOverrideHandler is a handle function that's triggered whenever the UTXO set
|
||||
// resets due to pruning point change via IBD.
|
||||
type OnPruningPointUTXOSetOverrideHandler func() error
|
||||
|
||||
// OnTransactionAddedToMempoolHandler is a handler function that's triggered
|
||||
// when a transaction is added to the mempool
|
||||
type OnTransactionAddedToMempoolHandler func()
|
||||
@@ -38,8 +42,9 @@ type FlowContext struct {
|
||||
|
||||
timeStarted int64
|
||||
|
||||
onBlockAddedToDAGHandler OnBlockAddedToDAGHandler
|
||||
onTransactionAddedToMempoolHandler OnTransactionAddedToMempoolHandler
|
||||
onBlockAddedToDAGHandler OnBlockAddedToDAGHandler
|
||||
onPruningPointUTXOSetOverrideHandler OnPruningPointUTXOSetOverrideHandler
|
||||
onTransactionAddedToMempoolHandler OnTransactionAddedToMempoolHandler
|
||||
|
||||
transactionsToRebroadcastLock sync.Mutex
|
||||
transactionsToRebroadcast map[externalapi.DomainTransactionID]*externalapi.DomainTransaction
|
||||
@@ -56,6 +61,8 @@ type FlowContext struct {
|
||||
|
||||
orphans map[externalapi.DomainHash]*externalapi.DomainBlock
|
||||
orphansMutex sync.RWMutex
|
||||
|
||||
shutdownChan chan struct{}
|
||||
}
|
||||
|
||||
// New returns a new instance of FlowContext.
|
||||
@@ -74,14 +81,31 @@ func New(cfg *config.Config, domain domain.Domain, addressManager *addressmanage
|
||||
transactionsToRebroadcast: make(map[externalapi.DomainTransactionID]*externalapi.DomainTransaction),
|
||||
orphans: make(map[externalapi.DomainHash]*externalapi.DomainBlock),
|
||||
timeStarted: mstime.Now().UnixMilliseconds(),
|
||||
shutdownChan: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Close signals to all flows the the protocol manager is closed.
|
||||
func (f *FlowContext) Close() {
|
||||
close(f.shutdownChan)
|
||||
}
|
||||
|
||||
// ShutdownChan is a chan where flows can subscribe to shutdown
|
||||
// event.
|
||||
func (f *FlowContext) ShutdownChan() <-chan struct{} {
|
||||
return f.shutdownChan
|
||||
}
|
||||
|
||||
// SetOnBlockAddedToDAGHandler sets the onBlockAddedToDAG handler
|
||||
func (f *FlowContext) SetOnBlockAddedToDAGHandler(onBlockAddedToDAGHandler OnBlockAddedToDAGHandler) {
|
||||
f.onBlockAddedToDAGHandler = onBlockAddedToDAGHandler
|
||||
}
|
||||
|
||||
// SetOnPruningPointUTXOSetOverrideHandler sets the onPruningPointUTXOSetOverrideHandler handler
|
||||
func (f *FlowContext) SetOnPruningPointUTXOSetOverrideHandler(onPruningPointUTXOSetOverrideHandler OnPruningPointUTXOSetOverrideHandler) {
|
||||
f.onPruningPointUTXOSetOverrideHandler = onPruningPointUTXOSetOverrideHandler
|
||||
}
|
||||
|
||||
// SetOnTransactionAddedToMempoolHandler sets the onTransactionAddedToMempool handler
|
||||
func (f *FlowContext) SetOnTransactionAddedToMempoolHandler(onTransactionAddedToMempoolHandler OnTransactionAddedToMempoolHandler) {
|
||||
f.onTransactionAddedToMempoolHandler = onTransactionAddedToMempoolHandler
|
||||
|
||||
@@ -2,8 +2,6 @@ package flowcontext
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/util/panics"
|
||||
)
|
||||
|
||||
var log, _ = logger.Get(logger.SubsystemTags.PROT)
|
||||
var spawn = panics.GoroutineWrapperFunc(log)
|
||||
var log = logger.RegisterSubSystem("PROT")
|
||||
|
||||
@@ -25,14 +25,17 @@ func (f *FlowContext) AddTransaction(tx *externalapi.DomainTransaction) error {
|
||||
return f.Broadcast(inv)
|
||||
}
|
||||
|
||||
func (f *FlowContext) updateTransactionsToRebroadcast(block *externalapi.DomainBlock) {
|
||||
func (f *FlowContext) updateTransactionsToRebroadcast(addedBlocks []*externalapi.DomainBlock) {
|
||||
f.transactionsToRebroadcastLock.Lock()
|
||||
defer f.transactionsToRebroadcastLock.Unlock()
|
||||
// Note: if the block is red, its transactions won't be rebroadcasted
|
||||
// anymore, although they are not included in the UTXO set.
|
||||
// This is probably ok, since red blocks are quite rare.
|
||||
for _, tx := range block.Transactions {
|
||||
delete(f.transactionsToRebroadcast, *consensushashing.TransactionID(tx))
|
||||
|
||||
for _, block := range addedBlocks {
|
||||
// Note: if a transaction is included in the DAG but not accepted,
|
||||
// it won't be rebroadcast anymore, although it is not included in
|
||||
// the UTXO set
|
||||
for _, tx := range block.Transactions {
|
||||
delete(f.transactionsToRebroadcast, *consensushashing.TransactionID(tx))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ type RelayInvsContext interface {
|
||||
Domain() domain.Domain
|
||||
Config() *config.Config
|
||||
OnNewBlock(block *externalapi.DomainBlock, blockInsertionResult *externalapi.BlockInsertionResult) error
|
||||
OnPruningPointUTXOSetOverride() error
|
||||
SharedRequestedBlocks() *SharedRequestedBlocks
|
||||
Broadcast(message appmessage.Message) error
|
||||
AddOrphan(orphanBlock *externalapi.DomainBlock)
|
||||
@@ -104,6 +105,11 @@ func (flow *handleRelayInvsFlow) start() error {
|
||||
continue
|
||||
}
|
||||
|
||||
err = flow.banIfBlockIsHeaderOnly(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("Processing block %s", inv.Hash)
|
||||
missingParents, blockInsertionResult, err := flow.processBlock(block)
|
||||
if err != nil {
|
||||
@@ -140,6 +146,15 @@ func (flow *handleRelayInvsFlow) start() error {
|
||||
}
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) banIfBlockIsHeaderOnly(block *externalapi.DomainBlock) error {
|
||||
if len(block.Transactions) == 0 {
|
||||
return protocolerrors.Errorf(true, "sent header of %s block where expected block with body",
|
||||
consensushashing.BlockHash(block))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) readInv() (*appmessage.MsgInvRelayBlock, error) {
|
||||
if len(flow.invsQueue) > 0 {
|
||||
var inv *appmessage.MsgInvRelayBlock
|
||||
|
||||
@@ -17,7 +17,7 @@ type RequestIBDBlocksContext interface {
|
||||
Domain() domain.Domain
|
||||
}
|
||||
|
||||
type handleRequestBlocksFlow struct {
|
||||
type handleRequestHeadersFlow struct {
|
||||
RequestIBDBlocksContext
|
||||
incomingRoute, outgoingRoute *router.Route
|
||||
peer *peer.Peer
|
||||
@@ -27,7 +27,7 @@ type handleRequestBlocksFlow struct {
|
||||
func HandleRequestHeaders(context RequestIBDBlocksContext, incomingRoute *router.Route,
|
||||
outgoingRoute *router.Route, peer *peer.Peer) error {
|
||||
|
||||
flow := &handleRequestBlocksFlow{
|
||||
flow := &handleRequestHeadersFlow{
|
||||
RequestIBDBlocksContext: context,
|
||||
incomingRoute: incomingRoute,
|
||||
outgoingRoute: outgoingRoute,
|
||||
@@ -36,7 +36,7 @@ func HandleRequestHeaders(context RequestIBDBlocksContext, incomingRoute *router
|
||||
return flow.start()
|
||||
}
|
||||
|
||||
func (flow *handleRequestBlocksFlow) start() error {
|
||||
func (flow *handleRequestHeadersFlow) start() error {
|
||||
for {
|
||||
lowHash, highHash, err := receiveRequestHeaders(flow.incomingRoute)
|
||||
if err != nil {
|
||||
@@ -50,7 +50,7 @@ func (flow *handleRequestBlocksFlow) start() error {
|
||||
// GetHashesBetween is a relatively heavy operation so we limit it
|
||||
// in order to avoid locking the consensus for too long
|
||||
const maxBlueScoreDifference = 1 << 10
|
||||
blockHashes, err := flow.Domain().Consensus().GetHashesBetween(lowHash, highHash, maxBlueScoreDifference)
|
||||
blockHashes, _, err := flow.Domain().Consensus().GetHashesBetween(lowHash, highHash, maxBlueScoreDifference)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -410,6 +410,11 @@ func (flow *handleRelayInvsFlow) fetchMissingUTXOSet(pruningPointHash *externala
|
||||
return false, protocolerrors.ConvertToBanningProtocolErrorIfRuleError(err, "error with pruning point UTXO set")
|
||||
}
|
||||
|
||||
err = flow.OnPruningPointUTXOSetOverride()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -533,6 +538,11 @@ func (flow *handleRelayInvsFlow) syncMissingBlockBodies(highHash *externalapi.Do
|
||||
return protocolerrors.Errorf(true, "expected block %s but got %s", expectedHash, blockHash)
|
||||
}
|
||||
|
||||
err = flow.banIfBlockIsHeaderOnly(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blockInsertionResult, err := flow.Domain().Consensus().ValidateAndInsertBlock(block)
|
||||
if err != nil {
|
||||
if errors.Is(err, ruleerrors.ErrDuplicateBlock) {
|
||||
|
||||
@@ -5,5 +5,5 @@ import (
|
||||
"github.com/kaspanet/kaspad/util/panics"
|
||||
)
|
||||
|
||||
var log, _ = logger.Get(logger.SubsystemTags.PROT)
|
||||
var log = logger.RegisterSubSystem("PROT")
|
||||
var spawn = panics.GoroutineWrapperFunc(log)
|
||||
|
||||
@@ -107,7 +107,7 @@ func handleError(err error, flowName string, isStopping *uint32, errChan chan er
|
||||
return
|
||||
}
|
||||
|
||||
if protocolErr := &(protocolerrors.ProtocolError{}); errors.As(err, &protocolErr) {
|
||||
if protocolErr := (protocolerrors.ProtocolError{}); errors.As(err, &protocolErr) {
|
||||
log.Errorf("Handshake protocol error from %s: %s", flowName, err)
|
||||
if atomic.AddUint32(isStopping, 1) == 1 {
|
||||
errChan <- err
|
||||
|
||||
@@ -5,5 +5,5 @@ import (
|
||||
"github.com/kaspanet/kaspad/util/panics"
|
||||
)
|
||||
|
||||
var log, _ = logger.Get(logger.SubsystemTags.PROT)
|
||||
var log = logger.RegisterSubSystem("PROT")
|
||||
var spawn = panics.GoroutineWrapperFunc(log)
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
// SendPingsContext is the interface for the context needed for the SendPings flow.
|
||||
type SendPingsContext interface {
|
||||
ShutdownChan() <-chan struct{}
|
||||
}
|
||||
|
||||
type sendPingsFlow struct {
|
||||
@@ -39,7 +40,13 @@ func (flow *sendPingsFlow) start() error {
|
||||
ticker := time.NewTicker(pingInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
for {
|
||||
select {
|
||||
case <-flow.ShutdownChan():
|
||||
return nil
|
||||
case <-ticker.C:
|
||||
}
|
||||
|
||||
nonce, err := random.Uint64()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -62,5 +69,4 @@ func (flow *sendPingsFlow) start() error {
|
||||
}
|
||||
flow.peer.SetPingIdle()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
"github.com/pkg/errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func checkFlowError(t *testing.T, err error, isProtocolError bool, shouldBan bool, contains string) {
|
||||
pErr := &protocolerrors.ProtocolError{}
|
||||
pErr := protocolerrors.ProtocolError{}
|
||||
if errors.As(err, &pErr) != isProtocolError {
|
||||
t.Fatalf("Unexepcted error %+v", err)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/app/protocol/flows/blockrelay"
|
||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
|
||||
@@ -24,6 +25,19 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var headerOnlyBlock = &externalapi.DomainBlock{
|
||||
Header: blockheader.NewImmutableBlockHeader(
|
||||
constants.MaxBlockVersion,
|
||||
[]*externalapi.DomainHash{externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})},
|
||||
&externalapi.DomainHash{},
|
||||
&externalapi.DomainHash{},
|
||||
&externalapi.DomainHash{},
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
),
|
||||
}
|
||||
|
||||
var orphanBlock = &externalapi.DomainBlock{
|
||||
Header: blockheader.NewImmutableBlockHeader(
|
||||
constants.MaxBlockVersion,
|
||||
@@ -35,6 +49,7 @@ var orphanBlock = &externalapi.DomainBlock{
|
||||
0,
|
||||
0,
|
||||
),
|
||||
Transactions: []*externalapi.DomainTransaction{{}},
|
||||
}
|
||||
|
||||
var validPruningPointBlock = &externalapi.DomainBlock{
|
||||
@@ -48,6 +63,7 @@ var validPruningPointBlock = &externalapi.DomainBlock{
|
||||
0,
|
||||
0,
|
||||
),
|
||||
Transactions: []*externalapi.DomainTransaction{{}},
|
||||
}
|
||||
|
||||
var invalidPruningPointBlock = &externalapi.DomainBlock{
|
||||
@@ -61,6 +77,7 @@ var invalidPruningPointBlock = &externalapi.DomainBlock{
|
||||
0,
|
||||
0,
|
||||
),
|
||||
Transactions: []*externalapi.DomainTransaction{{}},
|
||||
}
|
||||
|
||||
var unexpectedIBDBlock = &externalapi.DomainBlock{
|
||||
@@ -74,6 +91,7 @@ var unexpectedIBDBlock = &externalapi.DomainBlock{
|
||||
0,
|
||||
0,
|
||||
),
|
||||
Transactions: []*externalapi.DomainTransaction{{}},
|
||||
}
|
||||
|
||||
var invalidBlock = &externalapi.DomainBlock{
|
||||
@@ -87,6 +105,7 @@ var invalidBlock = &externalapi.DomainBlock{
|
||||
0,
|
||||
0,
|
||||
),
|
||||
Transactions: []*externalapi.DomainTransaction{{}},
|
||||
}
|
||||
|
||||
var unknownBlockHash = externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})
|
||||
@@ -95,6 +114,7 @@ var validPruningPointHash = consensushashing.BlockHash(validPruningPointBlock)
|
||||
var invalidBlockHash = consensushashing.BlockHash(invalidBlock)
|
||||
var invalidPruningPointHash = consensushashing.BlockHash(invalidPruningPointBlock)
|
||||
var orphanBlockHash = consensushashing.BlockHash(orphanBlock)
|
||||
var headerOnlyBlockHash = consensushashing.BlockHash(headerOnlyBlock)
|
||||
|
||||
type fakeRelayInvsContext struct {
|
||||
testName string
|
||||
@@ -110,6 +130,26 @@ type fakeRelayInvsContext struct {
|
||||
rwLock sync.RWMutex
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) EstimateNetworkHashesPerSecond(startHash *externalapi.DomainHash, windowSize int) (uint64, error) {
|
||||
panic(errors.Errorf("called unimplemented function from test '%s'", f.testName))
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) GetBlockEvenIfHeaderOnly(blockHash *externalapi.DomainHash) (*externalapi.DomainBlock, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) GetBlockRelations(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, *externalapi.DomainHash, []*externalapi.DomainHash, error) {
|
||||
panic(errors.Errorf("called unimplemented function from test '%s'", f.testName))
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) OnPruningPointUTXOSetOverride() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) GetVirtualUTXOs(expectedVirtualParents []*externalapi.DomainHash, fromOutpoint *externalapi.DomainOutpoint, limit int) ([]*externalapi.OutpointAndUTXOEntryPair, error) {
|
||||
panic(errors.Errorf("called unimplemented function from test '%s'", f.testName))
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) Anticone(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
|
||||
panic(errors.Errorf("called unimplemented function from test '%s'", f.testName))
|
||||
}
|
||||
@@ -150,7 +190,7 @@ func (f *fakeRelayInvsContext) GetBlockAcceptanceData(blockHash *externalapi.Dom
|
||||
panic(errors.Errorf("called unimplemented function from test '%s'", f.testName))
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) GetHashesBetween(lowHash, highHash *externalapi.DomainHash, maxBlueScoreDifference uint64) ([]*externalapi.DomainHash, error) {
|
||||
func (f *fakeRelayInvsContext) GetHashesBetween(lowHash, highHash *externalapi.DomainHash, maxBlueScoreDifference uint64) (hashes []*externalapi.DomainHash, actualHighHash *externalapi.DomainHash, err error) {
|
||||
panic(errors.Errorf("called unimplemented function from test '%s'", f.testName))
|
||||
}
|
||||
|
||||
@@ -450,6 +490,29 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
expectsBan: true,
|
||||
expectsErrToContain: "got unrequested block",
|
||||
},
|
||||
{
|
||||
name: "sending header only block on relay",
|
||||
funcToExecute: func(t *testing.T, incomingRoute, outgoingRoute *router.Route, context *fakeRelayInvsContext) {
|
||||
err := incomingRoute.Enqueue(appmessage.NewMsgInvBlock(headerOnlyBlockHash))
|
||||
if err != nil {
|
||||
t.Fatalf("Enqueue: %+v", err)
|
||||
}
|
||||
|
||||
msg, err := outgoingRoute.DequeueWithTimeout(time.Second)
|
||||
if err != nil {
|
||||
t.Fatalf("DequeueWithTimeout: %+v", err)
|
||||
}
|
||||
_ = msg.(*appmessage.MsgRequestRelayBlocks)
|
||||
|
||||
err = incomingRoute.Enqueue(appmessage.DomainBlockToMsgBlock(headerOnlyBlock))
|
||||
if err != nil {
|
||||
t.Fatalf("Enqueue: %+v", err)
|
||||
}
|
||||
},
|
||||
expectsProtocolError: true,
|
||||
expectsBan: true,
|
||||
expectsErrToContain: "block where expected block with body",
|
||||
},
|
||||
{
|
||||
name: "sending invalid block",
|
||||
funcToExecute: func(t *testing.T, incomingRoute, outgoingRoute *router.Route, context *fakeRelayInvsContext) {
|
||||
@@ -1442,7 +1505,7 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
for _, test := range tests {
|
||||
|
||||
// This is done to avoid race condition
|
||||
@@ -1457,7 +1520,7 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
errChan := make(chan error)
|
||||
context := &fakeRelayInvsContext{
|
||||
testName: test.name,
|
||||
params: params,
|
||||
params: &consensusConfig.Params,
|
||||
finishedIBD: make(chan struct{}),
|
||||
|
||||
trySetIBDRunningResponse: true,
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/protocol/flows/addressexchange"
|
||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type fakeReceiveAddressesContext struct{}
|
||||
@@ -19,7 +20,7 @@ func (f fakeReceiveAddressesContext) AddressManager() *addressmanager.AddressMan
|
||||
}
|
||||
|
||||
func TestReceiveAddressesErrors(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
incomingRoute := router.NewRoute()
|
||||
outgoingRoute := router.NewRoute()
|
||||
peer := peerpkg.New(nil)
|
||||
|
||||
@@ -191,7 +191,7 @@ func (flow *handleRelayedTransactionsFlow) receiveTransactions(requestedTransact
|
||||
continue
|
||||
}
|
||||
|
||||
return protocolerrors.Errorf(true, "rejected transaction %s", txID)
|
||||
return protocolerrors.Errorf(true, "rejected transaction %s: %s", txID, ruleErr)
|
||||
}
|
||||
err = flow.broadcastAcceptedTransactions([]*externalapi.DomainTransactionID{txID})
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
package transactionrelay_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/kaspanet/kaspad/app/protocol/flows/transactionrelay"
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/util/panics"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
)
|
||||
|
||||
type mocTransactionsRelayContext struct {
|
||||
netAdapter *netadapter.NetAdapter
|
||||
domain domain.Domain
|
||||
sharedRequestedTransactions *transactionrelay.SharedRequestedTransactions
|
||||
}
|
||||
|
||||
func (m *mocTransactionsRelayContext) NetAdapter() *netadapter.NetAdapter {
|
||||
return m.netAdapter
|
||||
}
|
||||
|
||||
func (m *mocTransactionsRelayContext) Domain() domain.Domain {
|
||||
return m.domain
|
||||
}
|
||||
|
||||
func (m *mocTransactionsRelayContext) SharedRequestedTransactions() *transactionrelay.SharedRequestedTransactions {
|
||||
return m.sharedRequestedTransactions
|
||||
}
|
||||
|
||||
func (m *mocTransactionsRelayContext) Broadcast(appmessage.Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mocTransactionsRelayContext) OnTransactionAddedToMempool() {
|
||||
}
|
||||
|
||||
// TestHandleRelayedTransactionsNotFound tests the flow of HandleRelayedTransactions when the peer doesn't
|
||||
// have the requested transactions in the mempool.
|
||||
func TestHandleRelayedTransactionsNotFound(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
|
||||
var log = logger.RegisterSubSystem("PROT")
|
||||
var spawn = panics.GoroutineWrapperFunc(log)
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestHandleRelayedTransactionsNotFound")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up test consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
sharedRequestedTransactions := transactionrelay.NewSharedRequestedTransactions()
|
||||
adapter, err := netadapter.NewNetAdapter(config.DefaultConfig())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a NetAdapter: %v", err)
|
||||
}
|
||||
domainInstance, err := domain.New(consensusConfig, tc.Database())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to set up a domain instance: %v", err)
|
||||
}
|
||||
context := &mocTransactionsRelayContext{
|
||||
netAdapter: adapter,
|
||||
domain: domainInstance,
|
||||
sharedRequestedTransactions: sharedRequestedTransactions,
|
||||
}
|
||||
incomingRoute := router.NewRoute()
|
||||
defer incomingRoute.Close()
|
||||
peerIncomingRoute := router.NewRoute()
|
||||
defer peerIncomingRoute.Close()
|
||||
|
||||
txID1 := externalapi.NewDomainTransactionIDFromByteArray(&[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, 0x01})
|
||||
txID2 := externalapi.NewDomainTransactionIDFromByteArray(&[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, 0x02})
|
||||
txIDs := []*externalapi.DomainTransactionID{txID1, txID2}
|
||||
invMessage := appmessage.NewMsgInvTransaction(txIDs)
|
||||
err = incomingRoute.Enqueue(invMessage)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error from incomingRoute.Enqueue: %v", err)
|
||||
}
|
||||
// The goroutine is representing the peer's actions.
|
||||
spawn("peerResponseToTheTransactionsRequest", func() {
|
||||
msg, err := peerIncomingRoute.Dequeue()
|
||||
if err != nil {
|
||||
t.Fatalf("Dequeue: %v", err)
|
||||
}
|
||||
inv := msg.(*appmessage.MsgRequestTransactions)
|
||||
|
||||
if len(txIDs) != len(inv.IDs) {
|
||||
t.Fatalf("TestHandleRelayedTransactions: expected %d transactions ID, but got %d", len(txIDs), len(inv.IDs))
|
||||
}
|
||||
|
||||
for i, id := range inv.IDs {
|
||||
if txIDs[i].String() != id.String() {
|
||||
t.Fatalf("TestHandleRelayedTransactions: expected equal txID: expected %s, but got %s", txIDs[i].String(), id.String())
|
||||
}
|
||||
err = incomingRoute.Enqueue(appmessage.NewMsgTransactionNotFound(txIDs[i]))
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error from incomingRoute.Enqueue: %v", err)
|
||||
}
|
||||
}
|
||||
// Insert an unexpected message type to stop the infinity loop.
|
||||
err = incomingRoute.Enqueue(&appmessage.MsgAddresses{})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error from incomingRoute.Enqueue: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
err = transactionrelay.HandleRelayedTransactions(context, incomingRoute, peerIncomingRoute)
|
||||
// Since we inserted an unexpected message type to stop the infinity loop,
|
||||
// we expect the error will be infected from this specific message and also the
|
||||
// error will count as a protocol message.
|
||||
if protocolErr := (protocolerrors.ProtocolError{}); err == nil || !errors.As(err, &protocolErr) {
|
||||
t.Fatalf("Expected to protocol error")
|
||||
} else {
|
||||
if !protocolErr.ShouldBan {
|
||||
t.Fatalf("Exepcted shouldBan true, but got false.")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "unexpected Addresses [code 3] message in the block relay flow while expecting an inv message") {
|
||||
t.Fatalf("Unexpected error: expected: an error due to existence of an Addresses message "+
|
||||
"in the block relay flow, but got: %v", protocolErr.Cause)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestOnClosedIncomingRoute verifies that an appropriate error message will be returned when
|
||||
// trying to dequeue a message from a closed route.
|
||||
func TestOnClosedIncomingRoute(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestOnClosedIncomingRoute")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up test consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
sharedRequestedTransactions := transactionrelay.NewSharedRequestedTransactions()
|
||||
adapter, err := netadapter.NewNetAdapter(config.DefaultConfig())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to creat a NetAdapter : %v", err)
|
||||
}
|
||||
domainInstance, err := domain.New(consensusConfig, tc.Database())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to set up a domain instance: %v", err)
|
||||
}
|
||||
context := &mocTransactionsRelayContext{
|
||||
netAdapter: adapter,
|
||||
domain: domainInstance,
|
||||
sharedRequestedTransactions: sharedRequestedTransactions,
|
||||
}
|
||||
incomingRoute := router.NewRoute()
|
||||
outgoingRoute := router.NewRoute()
|
||||
defer outgoingRoute.Close()
|
||||
|
||||
txID := externalapi.NewDomainTransactionIDFromByteArray(&[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, 0x01})
|
||||
txIDs := []*externalapi.DomainTransactionID{txID}
|
||||
|
||||
err = incomingRoute.Enqueue(&appmessage.MsgInvTransaction{TxIDs: txIDs})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error from incomingRoute.Enqueue: %v", err)
|
||||
}
|
||||
incomingRoute.Close()
|
||||
err = transactionrelay.HandleRelayedTransactions(context, incomingRoute, outgoingRoute)
|
||||
if err == nil || !errors.Is(err, router.ErrRouteClosed) {
|
||||
t.Fatalf("Unexpected error: expected: %v, got : %v", router.ErrRouteClosed, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package transactionrelay_test
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/protocol/flows/transactionrelay"
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
"github.com/kaspanet/kaspad/util/panics"
|
||||
"github.com/pkg/errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestHandleRequestedTransactionsNotFound tests the flow of HandleRequestedTransactions
|
||||
// when the requested transactions don't found in the mempool.
|
||||
func TestHandleRequestedTransactionsNotFound(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
var log = logger.RegisterSubSystem("PROT")
|
||||
var spawn = panics.GoroutineWrapperFunc(log)
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestHandleRequestedTransactionsNotFound")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up test Consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
sharedRequestedTransactions := transactionrelay.NewSharedRequestedTransactions()
|
||||
adapter, err := netadapter.NewNetAdapter(config.DefaultConfig())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a NetAdapter: %v", err)
|
||||
}
|
||||
domainInstance, err := domain.New(consensusConfig, tc.Database())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to set up a domain Instance: %v", err)
|
||||
}
|
||||
context := &mocTransactionsRelayContext{
|
||||
netAdapter: adapter,
|
||||
domain: domainInstance,
|
||||
sharedRequestedTransactions: sharedRequestedTransactions,
|
||||
}
|
||||
incomingRoute := router.NewRoute()
|
||||
outgoingRoute := router.NewRoute()
|
||||
defer outgoingRoute.Close()
|
||||
|
||||
txID1 := externalapi.NewDomainTransactionIDFromByteArray(&[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, 0x01})
|
||||
txID2 := externalapi.NewDomainTransactionIDFromByteArray(&[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, 0x02})
|
||||
txIDs := []*externalapi.DomainTransactionID{txID1, txID2}
|
||||
msg := appmessage.NewMsgRequestTransactions(txIDs)
|
||||
err = incomingRoute.Enqueue(msg)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error from incomingRoute.Enqueue: %v", err)
|
||||
}
|
||||
// The goroutine is representing the peer's actions.
|
||||
spawn("peerResponseToTheTransactionsMessages", func() {
|
||||
for i, id := range txIDs {
|
||||
msg, err := outgoingRoute.Dequeue()
|
||||
if err != nil {
|
||||
t.Fatalf("Dequeue: %s", err)
|
||||
}
|
||||
outMsg := msg.(*appmessage.MsgTransactionNotFound)
|
||||
if txIDs[i].String() != outMsg.ID.String() {
|
||||
t.Fatalf("TestHandleRelayedTransactions: expected equal txID: expected %s, but got %s", txIDs[i].String(), id.String())
|
||||
}
|
||||
}
|
||||
// Closed the incomingRoute for stop the infinity loop.
|
||||
incomingRoute.Close()
|
||||
})
|
||||
|
||||
err = transactionrelay.HandleRequestedTransactions(context, incomingRoute, outgoingRoute)
|
||||
// Make sure the error is due to the closed route.
|
||||
if err == nil || !errors.Is(err, router.ErrRouteClosed) {
|
||||
t.Fatalf("Unexpected error: expected: %v, got : %v", router.ErrRouteClosed, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -5,5 +5,5 @@ import (
|
||||
"github.com/kaspanet/kaspad/util/panics"
|
||||
)
|
||||
|
||||
var log, _ = logger.Get(logger.SubsystemTags.PROT)
|
||||
var log = logger.RegisterSubSystem("PROT")
|
||||
var spawn = panics.GoroutineWrapperFunc(log)
|
||||
|
||||
@@ -2,6 +2,9 @@ package protocol
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
|
||||
@@ -17,7 +20,9 @@ import (
|
||||
|
||||
// Manager manages the p2p protocol
|
||||
type Manager struct {
|
||||
context *flowcontext.FlowContext
|
||||
context *flowcontext.FlowContext
|
||||
routersWaitGroup sync.WaitGroup
|
||||
isClosed uint32
|
||||
}
|
||||
|
||||
// NewManager creates a new instance of the p2p protocol manager
|
||||
@@ -32,6 +37,18 @@ func NewManager(cfg *config.Config, domain domain.Domain, netAdapter *netadapter
|
||||
return &manager, nil
|
||||
}
|
||||
|
||||
// Close closes the protocol manager and waits until all p2p flows
|
||||
// finish.
|
||||
func (m *Manager) Close() {
|
||||
if !atomic.CompareAndSwapUint32(&m.isClosed, 0, 1) {
|
||||
panic(errors.New("The protocol manager was already closed"))
|
||||
}
|
||||
|
||||
atomic.StoreUint32(&m.isClosed, 1)
|
||||
m.context.Close()
|
||||
m.routersWaitGroup.Wait()
|
||||
}
|
||||
|
||||
// Peers returns the currently active peers
|
||||
func (m *Manager) Peers() []*peerpkg.Peer {
|
||||
return m.context.Peers()
|
||||
@@ -53,11 +70,13 @@ func (m *Manager) AddBlock(block *externalapi.DomainBlock) error {
|
||||
return m.context.AddBlock(block)
|
||||
}
|
||||
|
||||
func (m *Manager) runFlows(flows []*flow, peer *peerpkg.Peer, errChan <-chan error) error {
|
||||
func (m *Manager) runFlows(flows []*flow, peer *peerpkg.Peer, errChan <-chan error, flowsWaitGroup *sync.WaitGroup) error {
|
||||
flowsWaitGroup.Add(len(flows))
|
||||
for _, flow := range flows {
|
||||
executeFunc := flow.executeFunc // extract to new variable so that it's not overwritten
|
||||
spawn(fmt.Sprintf("flow-%s", flow.name), func() {
|
||||
executeFunc(peer)
|
||||
flowsWaitGroup.Done()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -69,6 +88,11 @@ func (m *Manager) SetOnBlockAddedToDAGHandler(onBlockAddedToDAGHandler flowconte
|
||||
m.context.SetOnBlockAddedToDAGHandler(onBlockAddedToDAGHandler)
|
||||
}
|
||||
|
||||
// SetOnPruningPointUTXOSetOverrideHandler sets the OnPruningPointUTXOSetOverride handler
|
||||
func (m *Manager) SetOnPruningPointUTXOSetOverrideHandler(onPruningPointUTXOSetOverrideHandler flowcontext.OnPruningPointUTXOSetOverrideHandler) {
|
||||
m.context.SetOnPruningPointUTXOSetOverrideHandler(onPruningPointUTXOSetOverrideHandler)
|
||||
}
|
||||
|
||||
// SetOnTransactionAddedToMempoolHandler sets the onTransactionAddedToMempool handler
|
||||
func (m *Manager) SetOnTransactionAddedToMempoolHandler(onTransactionAddedToMempoolHandler flowcontext.OnTransactionAddedToMempoolHandler) {
|
||||
m.context.SetOnTransactionAddedToMempoolHandler(onTransactionAddedToMempoolHandler)
|
||||
|
||||
@@ -2,8 +2,6 @@ package peer
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/util/panics"
|
||||
)
|
||||
|
||||
var log, _ = logger.Get(logger.SubsystemTags.PROT)
|
||||
var spawn = panics.GoroutineWrapperFunc(log)
|
||||
var log = logger.RegisterSubSystem("PROT")
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/protocol/flows/rejects"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/connmanager"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
@@ -10,10 +9,12 @@ import (
|
||||
"github.com/kaspanet/kaspad/app/protocol/flows/blockrelay"
|
||||
"github.com/kaspanet/kaspad/app/protocol/flows/handshake"
|
||||
"github.com/kaspanet/kaspad/app/protocol/flows/ping"
|
||||
"github.com/kaspanet/kaspad/app/protocol/flows/rejects"
|
||||
"github.com/kaspanet/kaspad/app/protocol/flows/transactionrelay"
|
||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/connmanager"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter"
|
||||
routerpkg "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
"github.com/pkg/errors"
|
||||
@@ -40,6 +41,13 @@ func (m *Manager) routerInitializer(router *routerpkg.Router, netConnection *net
|
||||
// After flows were registered - spawn a new thread that will wait for connection to finish initializing
|
||||
// and start receiving messages
|
||||
spawn("routerInitializer-runFlows", func() {
|
||||
m.routersWaitGroup.Add(1)
|
||||
defer m.routersWaitGroup.Done()
|
||||
|
||||
if atomic.LoadUint32(&m.isClosed) == 1 {
|
||||
panic(errors.Errorf("tried to initialize router when the protocol manager is closed"))
|
||||
}
|
||||
|
||||
isBanned, err := m.context.ConnectionManager().IsBanned(netConnection)
|
||||
if err != nil && !errors.Is(err, addressmanager.ErrAddressNotFound) {
|
||||
panic(err)
|
||||
@@ -58,29 +66,47 @@ func (m *Manager) routerInitializer(router *routerpkg.Router, netConnection *net
|
||||
|
||||
peer, err := handshake.HandleHandshake(m.context, netConnection, receiveVersionRoute,
|
||||
sendVersionRoute, router.OutgoingRoute())
|
||||
|
||||
if err != nil {
|
||||
m.handleError(err, netConnection, router.OutgoingRoute())
|
||||
// non-blocking read from channel
|
||||
select {
|
||||
case innerError := <-errChan:
|
||||
if errors.Is(err, routerpkg.ErrRouteClosed) {
|
||||
m.handleError(innerError, netConnection, router.OutgoingRoute())
|
||||
} else {
|
||||
log.Errorf("Peer %s sent invalid message: %s", netConnection, innerError)
|
||||
m.handleError(err, netConnection, router.OutgoingRoute())
|
||||
}
|
||||
default:
|
||||
m.handleError(err, netConnection, router.OutgoingRoute())
|
||||
}
|
||||
return
|
||||
}
|
||||
defer m.context.RemoveFromPeers(peer)
|
||||
|
||||
removeHandshakeRoutes(router)
|
||||
|
||||
err = m.runFlows(flows, peer, errChan)
|
||||
flowsWaitGroup := &sync.WaitGroup{}
|
||||
err = m.runFlows(flows, peer, errChan, flowsWaitGroup)
|
||||
if err != nil {
|
||||
m.handleError(err, netConnection, router.OutgoingRoute())
|
||||
// We call `flowsWaitGroup.Wait()` in two places instead of deferring, because
|
||||
// we already defer `m.routersWaitGroup.Done()`, so we try to avoid error prone
|
||||
// and confusing use of multiple dependent defers.
|
||||
flowsWaitGroup.Wait()
|
||||
return
|
||||
}
|
||||
flowsWaitGroup.Wait()
|
||||
})
|
||||
}
|
||||
|
||||
func (m *Manager) handleError(err error, netConnection *netadapter.NetConnection, outgoingRoute *routerpkg.Route) {
|
||||
if protocolErr := &(protocolerrors.ProtocolError{}); errors.As(err, &protocolErr) {
|
||||
if protocolErr := (protocolerrors.ProtocolError{}); errors.As(err, &protocolErr) {
|
||||
if !m.context.Config().DisableBanning && protocolErr.ShouldBan {
|
||||
log.Warnf("Banning %s (reason: %s)", netConnection, protocolErr.Cause)
|
||||
|
||||
err := m.context.ConnectionManager().Ban(netConnection)
|
||||
if !errors.Is(err, connmanager.ErrCannotBanPermanent) {
|
||||
if err != nil && !errors.Is(err, connmanager.ErrCannotBanPermanent) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -89,7 +115,7 @@ func (m *Manager) handleError(err error, netConnection *netadapter.NetConnection
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
log.Debugf("Disconnecting from %s (reason: %s)", netConnection, protocolErr.Cause)
|
||||
log.Infof("Disconnecting from %s (reason: %s)", netConnection, protocolErr.Cause)
|
||||
netConnection.Disconnect()
|
||||
return
|
||||
}
|
||||
@@ -225,7 +251,7 @@ func (m *Manager) registerTransactionRelayFlow(router *routerpkg.Router, isStopp
|
||||
outgoingRoute := router.OutgoingRoute()
|
||||
|
||||
return []*flow{
|
||||
m.registerFlow("HandleRelayedTransactions", router,
|
||||
m.registerFlowWithCapacity("HandleRelayedTransactions", 10_000, router,
|
||||
[]appmessage.MessageCommand{appmessage.CmdInvTransaction, appmessage.CmdTx, appmessage.CmdTransactionNotFound}, isStopping, errChan,
|
||||
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
|
||||
return transactionrelay.HandleRelayedTransactions(m.context, incomingRoute, outgoingRoute)
|
||||
@@ -261,6 +287,24 @@ func (m *Manager) registerFlow(name string, router *routerpkg.Router, messageTyp
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return m.registerFlowForRoute(route, name, isStopping, errChan, initializeFunc)
|
||||
}
|
||||
|
||||
func (m *Manager) registerFlowWithCapacity(name string, capacity int, router *routerpkg.Router,
|
||||
messageTypes []appmessage.MessageCommand, isStopping *uint32,
|
||||
errChan chan error, initializeFunc flowInitializeFunc) *flow {
|
||||
|
||||
route, err := router.AddIncomingRouteWithCapacity(capacity, messageTypes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return m.registerFlowForRoute(route, name, isStopping, errChan, initializeFunc)
|
||||
}
|
||||
|
||||
func (m *Manager) registerFlowForRoute(route *routerpkg.Route, name string, isStopping *uint32,
|
||||
errChan chan error, initializeFunc flowInitializeFunc) *flow {
|
||||
|
||||
return &flow{
|
||||
name: name,
|
||||
executeFunc: func(peer *peerpkg.Peer) {
|
||||
|
||||
@@ -12,19 +12,19 @@ type ProtocolError struct {
|
||||
Cause error
|
||||
}
|
||||
|
||||
func (e *ProtocolError) Error() string {
|
||||
func (e ProtocolError) Error() string {
|
||||
return e.Cause.Error()
|
||||
}
|
||||
|
||||
// Unwrap returns the cause of ProtocolError, to be used with `errors.Unwrap()`
|
||||
func (e *ProtocolError) Unwrap() error {
|
||||
func (e ProtocolError) Unwrap() error {
|
||||
return e.Cause
|
||||
}
|
||||
|
||||
// Errorf formats according to a format specifier and returns the string
|
||||
// as a ProtocolError.
|
||||
func Errorf(shouldBan bool, format string, args ...interface{}) error {
|
||||
return &ProtocolError{
|
||||
return ProtocolError{
|
||||
ShouldBan: shouldBan,
|
||||
Cause: errors.Errorf(format, args...),
|
||||
}
|
||||
@@ -33,7 +33,7 @@ func Errorf(shouldBan bool, format string, args ...interface{}) error {
|
||||
// New returns a ProtocolError with the supplied message.
|
||||
// New also records the stack trace at the point it was called.
|
||||
func New(shouldBan bool, message string) error {
|
||||
return &ProtocolError{
|
||||
return ProtocolError{
|
||||
ShouldBan: shouldBan,
|
||||
Cause: errors.New(message),
|
||||
}
|
||||
@@ -41,7 +41,7 @@ func New(shouldBan bool, message string) error {
|
||||
|
||||
// Wrap wraps the given error and returns it as a ProtocolError.
|
||||
func Wrap(shouldBan bool, err error, message string) error {
|
||||
return &ProtocolError{
|
||||
return ProtocolError{
|
||||
ShouldBan: shouldBan,
|
||||
Cause: errors.Wrap(err, message),
|
||||
}
|
||||
@@ -49,7 +49,7 @@ func Wrap(shouldBan bool, err error, message string) error {
|
||||
|
||||
// Wrapf wraps the given error with the given format and returns it as a ProtocolError.
|
||||
func Wrapf(shouldBan bool, err error, format string, args ...interface{}) error {
|
||||
return &ProtocolError{
|
||||
return ProtocolError{
|
||||
ShouldBan: shouldBan,
|
||||
Cause: errors.Wrapf(err, format, args...),
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@ import (
|
||||
"github.com/kaspanet/kaspad/util/panics"
|
||||
)
|
||||
|
||||
var log, _ = logger.Get(logger.SubsystemTags.RPCS)
|
||||
var log = logger.RegisterSubSystem("RPCS")
|
||||
var spawn = panics.GoroutineWrapperFunc(log)
|
||||
|
||||
@@ -64,20 +64,41 @@ func (m *Manager) NotifyBlockAddedToDAG(block *externalapi.DomainBlock, blockIns
|
||||
return err
|
||||
}
|
||||
|
||||
err = m.notifyVirtualDaaScoreChanged()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = m.notifyVirtualSelectedParentChainChanged(blockInsertionResult)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgBlock := appmessage.DomainBlockToMsgBlock(block)
|
||||
blockVerboseData, err := m.context.BuildBlockVerboseData(block.Header, block, false)
|
||||
rpcBlock := appmessage.DomainBlockToRPCBlock(block)
|
||||
err = m.context.PopulateBlockWithVerboseData(rpcBlock, block.Header, block, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
blockAddedNotification := appmessage.NewBlockAddedNotificationMessage(msgBlock, blockVerboseData)
|
||||
blockAddedNotification := appmessage.NewBlockAddedNotificationMessage(rpcBlock)
|
||||
return m.context.NotificationManager.NotifyBlockAdded(blockAddedNotification)
|
||||
}
|
||||
|
||||
// NotifyPruningPointUTXOSetOverride notifies the manager whenever the UTXO index
|
||||
// resets due to pruning point change via IBD.
|
||||
func (m *Manager) NotifyPruningPointUTXOSetOverride() error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyPruningPointUTXOSetOverride")
|
||||
defer onEnd()
|
||||
|
||||
if m.context.Config.UTXOIndex {
|
||||
err := m.notifyPruningPointUTXOSetOverride()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NotifyFinalityConflict notifies the manager that there's a finality conflict in the DAG
|
||||
func (m *Manager) NotifyFinalityConflict(violatingBlockHash string) error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyFinalityConflict")
|
||||
@@ -100,13 +121,25 @@ func (m *Manager) notifyUTXOsChanged(blockInsertionResult *externalapi.BlockInse
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyUTXOsChanged")
|
||||
defer onEnd()
|
||||
|
||||
utxoIndexChanges, err := m.context.UTXOIndex.Update(blockInsertionResult.VirtualSelectedParentChainChanges)
|
||||
utxoIndexChanges, err := m.context.UTXOIndex.Update(blockInsertionResult)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.context.NotificationManager.NotifyUTXOsChanged(utxoIndexChanges)
|
||||
}
|
||||
|
||||
func (m *Manager) notifyPruningPointUTXOSetOverride() error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.notifyPruningPointUTXOSetOverride")
|
||||
defer onEnd()
|
||||
|
||||
err := m.context.UTXOIndex.Reset()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.context.NotificationManager.NotifyPruningPointUTXOSetOverride()
|
||||
}
|
||||
|
||||
func (m *Manager) notifyVirtualSelectedParentBlueScoreChanged() error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyVirtualSelectedParentBlueScoreChanged")
|
||||
defer onEnd()
|
||||
@@ -125,6 +158,19 @@ func (m *Manager) notifyVirtualSelectedParentBlueScoreChanged() error {
|
||||
return m.context.NotificationManager.NotifyVirtualSelectedParentBlueScoreChanged(notification)
|
||||
}
|
||||
|
||||
func (m *Manager) notifyVirtualDaaScoreChanged() error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyVirtualDaaScoreChanged")
|
||||
defer onEnd()
|
||||
|
||||
virtualInfo, err := m.context.Domain.Consensus().GetVirtualInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
notification := appmessage.NewVirtualDaaScoreChangedNotificationMessage(virtualInfo.DAAScore)
|
||||
return m.context.NotificationManager.NotifyVirtualDaaScoreChanged(notification)
|
||||
}
|
||||
|
||||
func (m *Manager) notifyVirtualSelectedParentChainChanged(blockInsertionResult *externalapi.BlockInsertionResult) error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyVirtualSelectedParentChainChanged")
|
||||
defer onEnd()
|
||||
|
||||
@@ -42,6 +42,10 @@ var handlers = map[appmessage.MessageCommand]handler{
|
||||
appmessage.CmdBanRequestMessage: rpchandlers.HandleBan,
|
||||
appmessage.CmdUnbanRequestMessage: rpchandlers.HandleUnban,
|
||||
appmessage.CmdGetInfoRequestMessage: rpchandlers.HandleGetInfo,
|
||||
appmessage.CmdNotifyPruningPointUTXOSetOverrideRequestMessage: rpchandlers.HandleNotifyPruningPointUTXOSetOverrideRequest,
|
||||
appmessage.CmdStopNotifyingPruningPointUTXOSetOverrideRequestMessage: rpchandlers.HandleStopNotifyingPruningPointUTXOSetOverrideRequest,
|
||||
appmessage.CmdEstimateNetworkHashesPerSecondRequestMessage: rpchandlers.HandleEstimateNetworkHashesPerSecond,
|
||||
appmessage.CmdNotifyVirtualDaaScoreChangedRequestMessage: rpchandlers.HandleNotifyVirtualDaaScoreChanged,
|
||||
}
|
||||
|
||||
func (m *Manager) routerInitializer(router *router.Router, netConnection *netadapter.NetConnection) {
|
||||
|
||||
@@ -2,8 +2,6 @@ package rpccontext
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/util/panics"
|
||||
)
|
||||
|
||||
var log, _ = logger.Get(logger.SubsystemTags.RPCS)
|
||||
var spawn = panics.GoroutineWrapperFunc(log)
|
||||
var log = logger.RegisterSubSystem("RPCS")
|
||||
|
||||
@@ -30,6 +30,8 @@ type NotificationListener struct {
|
||||
propagateFinalityConflictResolvedNotifications bool
|
||||
propagateUTXOsChangedNotifications bool
|
||||
propagateVirtualSelectedParentBlueScoreChangedNotifications bool
|
||||
propagateVirtualDaaScoreChangedNotifications bool
|
||||
propagatePruningPointUTXOSetOverrideNotifications bool
|
||||
|
||||
propagateUTXOsChangedNotificationAddresses map[utxoindex.ScriptPublicKeyString]*UTXOsChangedNotificationAddress
|
||||
}
|
||||
@@ -180,6 +182,42 @@ func (nm *NotificationManager) NotifyVirtualSelectedParentBlueScoreChanged(
|
||||
return nil
|
||||
}
|
||||
|
||||
// NotifyVirtualDaaScoreChanged notifies the notification manager that the DAG's
|
||||
// virtual DAA score has changed
|
||||
func (nm *NotificationManager) NotifyVirtualDaaScoreChanged(
|
||||
notification *appmessage.VirtualDaaScoreChangedNotificationMessage) error {
|
||||
|
||||
nm.RLock()
|
||||
defer nm.RUnlock()
|
||||
|
||||
for router, listener := range nm.listeners {
|
||||
if listener.propagateVirtualDaaScoreChangedNotifications {
|
||||
err := router.OutgoingRoute().Enqueue(notification)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NotifyPruningPointUTXOSetOverride notifies the notification manager that the UTXO index
|
||||
// reset due to pruning point change via IBD.
|
||||
func (nm *NotificationManager) NotifyPruningPointUTXOSetOverride() error {
|
||||
nm.RLock()
|
||||
defer nm.RUnlock()
|
||||
|
||||
for router, listener := range nm.listeners {
|
||||
if listener.propagatePruningPointUTXOSetOverrideNotifications {
|
||||
err := router.OutgoingRoute().Enqueue(appmessage.NewPruningPointUTXOSetOverrideNotificationMessage())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newNotificationListener() *NotificationListener {
|
||||
return &NotificationListener{
|
||||
propagateBlockAddedNotifications: false,
|
||||
@@ -188,6 +226,7 @@ func newNotificationListener() *NotificationListener {
|
||||
propagateFinalityConflictResolvedNotifications: false,
|
||||
propagateUTXOsChangedNotifications: false,
|
||||
propagateVirtualSelectedParentBlueScoreChangedNotifications: false,
|
||||
propagatePruningPointUTXOSetOverrideNotifications: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,3 +327,21 @@ func (nl *NotificationListener) convertUTXOChangesToUTXOsChangedNotification(
|
||||
func (nl *NotificationListener) PropagateVirtualSelectedParentBlueScoreChangedNotifications() {
|
||||
nl.propagateVirtualSelectedParentBlueScoreChangedNotifications = true
|
||||
}
|
||||
|
||||
// PropagateVirtualDaaScoreChangedNotifications instructs the listener to send
|
||||
// virtual DAA score notifications to the remote listener
|
||||
func (nl *NotificationListener) PropagateVirtualDaaScoreChangedNotifications() {
|
||||
nl.propagateVirtualDaaScoreChangedNotifications = true
|
||||
}
|
||||
|
||||
// PropagatePruningPointUTXOSetOverrideNotifications instructs the listener to send pruning point UTXO set override notifications
|
||||
// to the remote listener.
|
||||
func (nl *NotificationListener) PropagatePruningPointUTXOSetOverrideNotifications() {
|
||||
nl.propagatePruningPointUTXOSetOverrideNotifications = true
|
||||
}
|
||||
|
||||
// StopPropagatingPruningPointUTXOSetOverrideNotifications instructs the listener to stop sending pruning
|
||||
// point UTXO set override notifications to the remote listener.
|
||||
func (nl *NotificationListener) StopPropagatingPruningPointUTXOSetOverrideNotifications() {
|
||||
nl.propagatePruningPointUTXOSetOverrideNotifications = false
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ func ConvertUTXOOutpointEntryPairsToUTXOsByAddressesEntries(address string, pair
|
||||
UTXOEntry: &appmessage.RPCUTXOEntry{
|
||||
Amount: utxoEntry.Amount(),
|
||||
ScriptPublicKey: &appmessage.RPCScriptPublicKey{Script: hex.EncodeToString(utxoEntry.ScriptPublicKey().Script), Version: utxoEntry.ScriptPublicKey().Version},
|
||||
BlockBlueScore: utxoEntry.BlockBlueScore(),
|
||||
BlockDAAScore: utxoEntry.BlockDAAScore(),
|
||||
IsCoinbase: utxoEntry.IsCoinbase(),
|
||||
},
|
||||
})
|
||||
|
||||
@@ -2,22 +2,17 @@ package rpccontext
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
"github.com/pkg/errors"
|
||||
"math"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
difficultyPackage "github.com/kaspanet/kaspad/util/difficulty"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/estimatedsize"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
@@ -27,73 +22,6 @@ import (
|
||||
// ErrBuildBlockVerboseDataInvalidBlock indicates that a block that was given to BuildBlockVerboseData is invalid.
|
||||
var ErrBuildBlockVerboseDataInvalidBlock = errors.New("ErrBuildBlockVerboseDataInvalidBlock")
|
||||
|
||||
// BuildBlockVerboseData builds a BlockVerboseData from the given blockHeader.
|
||||
// A block may optionally also be given if it's available in the calling context.
|
||||
func (ctx *Context) BuildBlockVerboseData(blockHeader externalapi.BlockHeader, block *externalapi.DomainBlock,
|
||||
includeTransactionVerboseData bool) (*appmessage.BlockVerboseData, error) {
|
||||
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "BuildBlockVerboseData")
|
||||
defer onEnd()
|
||||
|
||||
hash := consensushashing.HeaderHash(blockHeader)
|
||||
|
||||
blockInfo, err := ctx.Domain.Consensus().GetBlockInfo(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if blockInfo.BlockStatus == externalapi.StatusInvalid {
|
||||
return nil, errors.Wrap(ErrBuildBlockVerboseDataInvalidBlock, "cannot build verbose data for "+
|
||||
"invalid block")
|
||||
}
|
||||
|
||||
result := &appmessage.BlockVerboseData{
|
||||
Hash: hash.String(),
|
||||
Version: blockHeader.Version(),
|
||||
VersionHex: fmt.Sprintf("%08x", blockHeader.Version()),
|
||||
HashMerkleRoot: blockHeader.HashMerkleRoot().String(),
|
||||
AcceptedIDMerkleRoot: blockHeader.AcceptedIDMerkleRoot().String(),
|
||||
UTXOCommitment: blockHeader.UTXOCommitment().String(),
|
||||
ParentHashes: hashes.ToStrings(blockHeader.ParentHashes()),
|
||||
Nonce: blockHeader.Nonce(),
|
||||
Time: blockHeader.TimeInMilliseconds(),
|
||||
Bits: strconv.FormatInt(int64(blockHeader.Bits()), 16),
|
||||
Difficulty: ctx.GetDifficultyRatio(blockHeader.Bits(), ctx.Config.ActiveNetParams),
|
||||
BlueScore: blockInfo.BlueScore,
|
||||
IsHeaderOnly: blockInfo.BlockStatus == externalapi.StatusHeaderOnly,
|
||||
}
|
||||
|
||||
if blockInfo.BlockStatus != externalapi.StatusHeaderOnly {
|
||||
if block == nil {
|
||||
block, err = ctx.Domain.Consensus().GetBlock(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
txIDs := make([]string, len(block.Transactions))
|
||||
for i, tx := range block.Transactions {
|
||||
txIDs[i] = consensushashing.TransactionID(tx).String()
|
||||
}
|
||||
result.TxIDs = txIDs
|
||||
|
||||
if includeTransactionVerboseData {
|
||||
transactionVerboseData := make([]*appmessage.TransactionVerboseData, len(block.Transactions))
|
||||
for i, tx := range block.Transactions {
|
||||
txID := consensushashing.TransactionID(tx).String()
|
||||
data, err := ctx.BuildTransactionVerboseData(tx, txID, blockHeader, hash.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transactionVerboseData[i] = data
|
||||
}
|
||||
result.TransactionVerboseData = transactionVerboseData
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetDifficultyRatio returns the proof-of-work difficulty as a multiple of the
|
||||
// minimum difficulty using the passed bits field from the header of a block.
|
||||
func (ctx *Context) GetDifficultyRatio(bits uint32, params *dagconfig.Params) float64 {
|
||||
@@ -101,7 +29,7 @@ func (ctx *Context) GetDifficultyRatio(bits uint32, params *dagconfig.Params) fl
|
||||
// converted back to a number. Note this is not the same as the proof of
|
||||
// work limit directly because the block difficulty is encoded in a block
|
||||
// with the compact form which loses precision.
|
||||
target := difficulty.CompactToBig(bits)
|
||||
target := difficultyPackage.CompactToBig(bits)
|
||||
|
||||
difficulty := new(big.Rat).SetFrac(params.PowMax, target)
|
||||
diff, _ := difficulty.Float64()
|
||||
@@ -112,105 +40,128 @@ func (ctx *Context) GetDifficultyRatio(bits uint32, params *dagconfig.Params) fl
|
||||
return diff
|
||||
}
|
||||
|
||||
// BuildTransactionVerboseData builds a TransactionVerboseData from
|
||||
// the given parameters
|
||||
func (ctx *Context) BuildTransactionVerboseData(tx *externalapi.DomainTransaction, txID string,
|
||||
blockHeader externalapi.BlockHeader, blockHash string) (
|
||||
*appmessage.TransactionVerboseData, error) {
|
||||
// PopulateBlockWithVerboseData populates the given `block` with verbose
|
||||
// data from `domainBlockHeader` and optionally from `domainBlock`
|
||||
func (ctx *Context) PopulateBlockWithVerboseData(block *appmessage.RPCBlock, domainBlockHeader externalapi.BlockHeader,
|
||||
domainBlock *externalapi.DomainBlock, includeTransactionVerboseData bool) error {
|
||||
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "BuildTransactionVerboseData")
|
||||
defer onEnd()
|
||||
blockHash := consensushashing.HeaderHash(domainBlockHeader)
|
||||
|
||||
var payloadHash string
|
||||
if tx.SubnetworkID != subnetworks.SubnetworkIDNative {
|
||||
payloadHash = tx.PayloadHash.String()
|
||||
blockInfo, err := ctx.Domain.Consensus().GetBlockInfo(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
txReply := &appmessage.TransactionVerboseData{
|
||||
TxID: txID,
|
||||
Hash: consensushashing.TransactionHash(tx).String(),
|
||||
Size: estimatedsize.TransactionEstimatedSerializedSize(tx),
|
||||
TransactionVerboseInputs: ctx.buildTransactionVerboseInputs(tx),
|
||||
TransactionVerboseOutputs: ctx.buildTransactionVerboseOutputs(tx, nil),
|
||||
Version: tx.Version,
|
||||
LockTime: tx.LockTime,
|
||||
SubnetworkID: tx.SubnetworkID.String(),
|
||||
Gas: tx.Gas,
|
||||
PayloadHash: payloadHash,
|
||||
Payload: hex.EncodeToString(tx.Payload),
|
||||
if blockInfo.BlockStatus == externalapi.StatusInvalid {
|
||||
return errors.Wrap(ErrBuildBlockVerboseDataInvalidBlock, "cannot build verbose data for "+
|
||||
"invalid block")
|
||||
}
|
||||
|
||||
if blockHeader != nil {
|
||||
txReply.Time = uint64(blockHeader.TimeInMilliseconds())
|
||||
txReply.BlockTime = uint64(blockHeader.TimeInMilliseconds())
|
||||
txReply.BlockHash = blockHash
|
||||
_, selectedParentHash, childrenHashes, err := ctx.Domain.Consensus().GetBlockRelations(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return txReply, nil
|
||||
}
|
||||
block.VerboseData = &appmessage.RPCBlockVerboseData{
|
||||
Hash: blockHash.String(),
|
||||
Difficulty: ctx.GetDifficultyRatio(domainBlockHeader.Bits(), ctx.Config.ActiveNetParams),
|
||||
ChildrenHashes: hashes.ToStrings(childrenHashes),
|
||||
IsHeaderOnly: blockInfo.BlockStatus == externalapi.StatusHeaderOnly,
|
||||
BlueScore: blockInfo.BlueScore,
|
||||
}
|
||||
// selectedParentHash will be nil in the genesis block
|
||||
if selectedParentHash != nil {
|
||||
block.VerboseData.SelectedParentHash = selectedParentHash.String()
|
||||
}
|
||||
|
||||
func (ctx *Context) buildTransactionVerboseInputs(tx *externalapi.DomainTransaction) []*appmessage.TransactionVerboseInput {
|
||||
inputs := make([]*appmessage.TransactionVerboseInput, len(tx.Inputs))
|
||||
for i, transactionInput := range tx.Inputs {
|
||||
// The disassembled string will contain [error] inline
|
||||
// if the script doesn't fully parse, so ignore the
|
||||
// error here.
|
||||
disbuf, _ := txscript.DisasmString(constants.MaxScriptPublicKeyVersion, transactionInput.SignatureScript)
|
||||
if blockInfo.BlockStatus == externalapi.StatusHeaderOnly {
|
||||
return nil
|
||||
}
|
||||
|
||||
input := &appmessage.TransactionVerboseInput{}
|
||||
input.TxID = transactionInput.PreviousOutpoint.TransactionID.String()
|
||||
input.OutputIndex = transactionInput.PreviousOutpoint.Index
|
||||
input.Sequence = transactionInput.Sequence
|
||||
input.ScriptSig = &appmessage.ScriptSig{
|
||||
Asm: disbuf,
|
||||
Hex: hex.EncodeToString(transactionInput.SignatureScript),
|
||||
// Get the block if we didn't receive it previously
|
||||
if domainBlock == nil {
|
||||
domainBlock, err = ctx.Domain.Consensus().GetBlockEvenIfHeaderOnly(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
inputs[i] = input
|
||||
}
|
||||
|
||||
return inputs
|
||||
}
|
||||
transactionIDs := make([]string, len(domainBlock.Transactions))
|
||||
for i, transaction := range domainBlock.Transactions {
|
||||
transactionIDs[i] = consensushashing.TransactionID(transaction).String()
|
||||
}
|
||||
block.VerboseData.TransactionIDs = transactionIDs
|
||||
|
||||
// buildTransactionVerboseOutputs returns a slice of JSON objects for the outputs of the passed
|
||||
// transaction.
|
||||
func (ctx *Context) buildTransactionVerboseOutputs(tx *externalapi.DomainTransaction, filterAddrMap map[string]struct{}) []*appmessage.TransactionVerboseOutput {
|
||||
outputs := make([]*appmessage.TransactionVerboseOutput, len(tx.Outputs))
|
||||
for i, transactionOutput := range tx.Outputs {
|
||||
|
||||
// Ignore the error here since an error means the script
|
||||
// couldn't parse and there is no additional information about
|
||||
// it anyways.
|
||||
scriptClass, addr, _ := txscript.ExtractScriptPubKeyAddress(
|
||||
transactionOutput.ScriptPublicKey, ctx.Config.ActiveNetParams)
|
||||
|
||||
// Encode the addresses while checking if the address passes the
|
||||
// filter when needed.
|
||||
passesFilter := len(filterAddrMap) == 0
|
||||
var encodedAddr string
|
||||
if addr != nil {
|
||||
encodedAddr = addr.EncodeAddress()
|
||||
|
||||
// If the filter doesn't already pass, make it pass if
|
||||
// the address exists in the filter.
|
||||
if _, exists := filterAddrMap[encodedAddr]; exists {
|
||||
passesFilter = true
|
||||
if includeTransactionVerboseData {
|
||||
for _, transaction := range block.Transactions {
|
||||
err := ctx.PopulateTransactionWithVerboseData(transaction, domainBlockHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !passesFilter {
|
||||
continue
|
||||
}
|
||||
|
||||
output := &appmessage.TransactionVerboseOutput{}
|
||||
output.Index = uint32(i)
|
||||
output.Value = transactionOutput.Value
|
||||
output.ScriptPubKey = &appmessage.ScriptPubKeyResult{
|
||||
Address: encodedAddr,
|
||||
Hex: hex.EncodeToString(transactionOutput.ScriptPublicKey.Script),
|
||||
Type: scriptClass.String(),
|
||||
}
|
||||
outputs[i] = output
|
||||
}
|
||||
|
||||
return outputs
|
||||
return nil
|
||||
}
|
||||
|
||||
// PopulateTransactionWithVerboseData populates the given `transaction` with
|
||||
// verbose data from `domainTransaction`
|
||||
func (ctx *Context) PopulateTransactionWithVerboseData(
|
||||
transaction *appmessage.RPCTransaction, domainBlockHeader externalapi.BlockHeader) error {
|
||||
|
||||
domainTransaction, err := appmessage.RPCTransactionToDomainTransaction(transaction)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
transaction.VerboseData = &appmessage.RPCTransactionVerboseData{
|
||||
TransactionID: consensushashing.TransactionID(domainTransaction).String(),
|
||||
Hash: consensushashing.TransactionHash(domainTransaction).String(),
|
||||
Size: estimatedsize.TransactionEstimatedSerializedSize(domainTransaction),
|
||||
}
|
||||
if domainBlockHeader != nil {
|
||||
transaction.VerboseData.BlockHash = consensushashing.HeaderHash(domainBlockHeader).String()
|
||||
transaction.VerboseData.BlockTime = uint64(domainBlockHeader.TimeInMilliseconds())
|
||||
}
|
||||
for _, input := range transaction.Inputs {
|
||||
ctx.populateTransactionInputWithVerboseData(input)
|
||||
}
|
||||
for _, output := range transaction.Outputs {
|
||||
err := ctx.populateTransactionOutputWithVerboseData(output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctx *Context) populateTransactionInputWithVerboseData(transactionInput *appmessage.RPCTransactionInput) {
|
||||
transactionInput.VerboseData = &appmessage.RPCTransactionInputVerboseData{}
|
||||
}
|
||||
|
||||
func (ctx *Context) populateTransactionOutputWithVerboseData(transactionOutput *appmessage.RPCTransactionOutput) error {
|
||||
scriptPublicKey, err := hex.DecodeString(transactionOutput.ScriptPublicKey.Script)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
domainScriptPublicKey := &externalapi.ScriptPublicKey{
|
||||
Script: scriptPublicKey,
|
||||
Version: transactionOutput.ScriptPublicKey.Version,
|
||||
}
|
||||
|
||||
// Ignore the error here since an error means the script
|
||||
// couldn't be parsed and there's no additional information about
|
||||
// it anyways
|
||||
scriptPublicKeyType, scriptPublicKeyAddress, _ := txscript.ExtractScriptPubKeyAddress(
|
||||
domainScriptPublicKey, ctx.Config.ActiveNetParams)
|
||||
|
||||
var encodedScriptPublicKeyAddress string
|
||||
if scriptPublicKeyAddress != nil {
|
||||
encodedScriptPublicKeyAddress = scriptPublicKeyAddress.EncodeAddress()
|
||||
}
|
||||
transactionOutput.VerboseData = &appmessage.RPCTransactionOutputVerboseData{
|
||||
ScriptPublicKeyType: scriptPublicKeyType.String(),
|
||||
ScriptPublicKeyAddress: encodedScriptPublicKeyAddress,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
39
app/rpc/rpchandlers/estimate_network_hashes_per_second.go
Normal file
39
app/rpc/rpchandlers/estimate_network_hashes_per_second.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package rpchandlers
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
)
|
||||
|
||||
// HandleEstimateNetworkHashesPerSecond handles the respectively named RPC command
|
||||
func HandleEstimateNetworkHashesPerSecond(
|
||||
context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||
|
||||
estimateNetworkHashesPerSecondRequest := request.(*appmessage.EstimateNetworkHashesPerSecondRequestMessage)
|
||||
|
||||
windowSize := int(estimateNetworkHashesPerSecondRequest.WindowSize)
|
||||
startHash := model.VirtualBlockHash
|
||||
if estimateNetworkHashesPerSecondRequest.StartHash != "" {
|
||||
var err error
|
||||
startHash, err = externalapi.NewDomainHashFromString(estimateNetworkHashesPerSecondRequest.StartHash)
|
||||
if err != nil {
|
||||
response := &appmessage.EstimateNetworkHashesPerSecondResponseMessage{}
|
||||
response.Error = appmessage.RPCErrorf("StartHash '%s' is not a valid block hash",
|
||||
estimateNetworkHashesPerSecondRequest.StartHash)
|
||||
return response, nil
|
||||
}
|
||||
}
|
||||
|
||||
networkHashesPerSecond, err := context.Domain.Consensus().EstimateNetworkHashesPerSecond(startHash, windowSize)
|
||||
if err != nil {
|
||||
response := &appmessage.EstimateNetworkHashesPerSecondResponseMessage{}
|
||||
response.Error = appmessage.RPCErrorf("could not resolve network hashes per "+
|
||||
"second for startHash %s and window size %d: %s", startHash, windowSize, err)
|
||||
return response, nil
|
||||
}
|
||||
|
||||
return appmessage.NewEstimateNetworkHashesPerSecondResponseMessage(networkHashesPerSecond), nil
|
||||
}
|
||||
@@ -20,7 +20,7 @@ func HandleGetBlock(context *rpccontext.Context, _ *router.Router, request appme
|
||||
return errorMessage, nil
|
||||
}
|
||||
|
||||
header, err := context.Domain.Consensus().GetBlockHeader(hash)
|
||||
block, err := context.Domain.Consensus().GetBlockEvenIfHeaderOnly(hash)
|
||||
if err != nil {
|
||||
errorMessage := &appmessage.GetBlockResponseMessage{}
|
||||
errorMessage.Error = appmessage.RPCErrorf("Block %s not found", hash)
|
||||
@@ -29,7 +29,13 @@ func HandleGetBlock(context *rpccontext.Context, _ *router.Router, request appme
|
||||
|
||||
response := appmessage.NewGetBlockResponseMessage()
|
||||
|
||||
blockVerboseData, err := context.BuildBlockVerboseData(header, nil, getBlockRequest.IncludeTransactionVerboseData)
|
||||
if getBlockRequest.IncludeTransactions {
|
||||
response.Block = appmessage.DomainBlockToRPCBlock(block)
|
||||
} else {
|
||||
response.Block = appmessage.DomainBlockToRPCBlock(&externalapi.DomainBlock{Header: block.Header})
|
||||
}
|
||||
|
||||
err = context.PopulateBlockWithVerboseData(response.Block, block.Header, block, getBlockRequest.IncludeTransactions)
|
||||
if err != nil {
|
||||
if errors.Is(err, rpccontext.ErrBuildBlockVerboseDataInvalidBlock) {
|
||||
errorMessage := &appmessage.GetBlockResponseMessage{}
|
||||
@@ -39,7 +45,5 @@ func HandleGetBlock(context *rpccontext.Context, _ *router.Router, request appme
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response.BlockVerboseData = blockVerboseData
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -35,6 +35,13 @@ func HandleGetBlockDAGInfo(context *rpccontext.Context, _ *router.Router, _ appm
|
||||
response.VirtualParentHashes = hashes.ToStrings(virtualInfo.ParentHashes)
|
||||
response.Difficulty = context.GetDifficultyRatio(virtualInfo.Bits, context.Config.ActiveNetParams)
|
||||
response.PastMedianTime = virtualInfo.PastMedianTime
|
||||
response.VirtualDAAScore = virtualInfo.DAAScore
|
||||
|
||||
pruningPoint, err := context.Domain.Consensus().PruningPoint()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.PruningPointHash = pruningPoint.String()
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -31,12 +31,12 @@ func HandleGetBlockTemplate(context *rpccontext.Context, _ *router.Router, reque
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msgBlock := appmessage.DomainBlockToMsgBlock(templateBlock)
|
||||
rpcBlock := appmessage.DomainBlockToRPCBlock(templateBlock)
|
||||
|
||||
isSynced, err := context.ProtocolManager.ShouldMine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return appmessage.NewGetBlockTemplateResponseMessage(msgBlock, isSynced), nil
|
||||
return appmessage.NewGetBlockTemplateResponseMessage(rpcBlock, isSynced), nil
|
||||
}
|
||||
|
||||
@@ -11,18 +11,18 @@ import (
|
||||
const (
|
||||
// maxBlocksInGetBlocksResponse is the max amount of blocks that are
|
||||
// allowed in a GetBlocksResult.
|
||||
maxBlocksInGetBlocksResponse = 100
|
||||
maxBlocksInGetBlocksResponse = 1000
|
||||
)
|
||||
|
||||
// HandleGetBlocks handles the respectively named RPC command
|
||||
func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||
getBlocksRequest := request.(*appmessage.GetBlocksRequestMessage)
|
||||
|
||||
// Validate that user didn't set IncludeTransactionVerboseData without setting IncludeBlockVerboseData
|
||||
if !getBlocksRequest.IncludeBlockVerboseData && getBlocksRequest.IncludeTransactionVerboseData {
|
||||
// Validate that user didn't set IncludeTransactions without setting IncludeBlocks
|
||||
if !getBlocksRequest.IncludeBlocks && getBlocksRequest.IncludeTransactions {
|
||||
return &appmessage.GetBlocksResponseMessage{
|
||||
Error: appmessage.RPCErrorf(
|
||||
"If includeTransactionVerboseData is set, then includeBlockVerboseData must be set as well"),
|
||||
"If includeTransactions is set, then includeBlockVerboseData must be set as well"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -37,6 +37,17 @@ func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appm
|
||||
Error: appmessage.RPCErrorf("Could not decode lowHash %s: %s", getBlocksRequest.LowHash, err),
|
||||
}, nil
|
||||
}
|
||||
|
||||
blockInfo, err := context.Domain.Consensus().GetBlockInfo(lowHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !blockInfo.Exists {
|
||||
return &appmessage.GetBlocksResponseMessage{
|
||||
Error: appmessage.RPCErrorf("Could not find lowHash %s", getBlocksRequest.LowHash),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Get hashes between lowHash and virtualSelectedParent
|
||||
@@ -44,8 +55,7 @@ func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appm
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockHashes, err := context.Domain.Consensus().GetHashesBetween(
|
||||
lowHash, virtualSelectedParent, maxBlocksInGetBlocksResponse)
|
||||
blockHashes, highHash, err := context.Domain.Consensus().GetHashesBetween(lowHash, virtualSelectedParent, maxBlocksInGetBlocksResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -53,9 +63,10 @@ func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appm
|
||||
// prepend low hash to make it inclusive
|
||||
blockHashes = append([]*externalapi.DomainHash{lowHash}, blockHashes...)
|
||||
|
||||
// If there are no maxBlocksInGetBlocksResponse between lowHash and virtualSelectedParent -
|
||||
// add virtualSelectedParent's anticone
|
||||
if len(blockHashes) < maxBlocksInGetBlocksResponse {
|
||||
// If the high hash is equal to virtualSelectedParent it means GetHashesBetween didn't skip any hashes, and
|
||||
// there's space to add the virtualSelectedParent's anticone, otherwise you can't add the anticone because
|
||||
// there's no guarantee that all of the anticone root ancestors will be present.
|
||||
if highHash.Equal(virtualSelectedParent) {
|
||||
virtualSelectedParentAnticone, err := context.Domain.Consensus().Anticone(virtualSelectedParent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -70,26 +81,28 @@ func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appm
|
||||
}
|
||||
|
||||
// Prepare the response
|
||||
response := &appmessage.GetBlocksResponseMessage{
|
||||
BlockHashes: hashes.ToStrings(blockHashes),
|
||||
}
|
||||
|
||||
// Retrieve all block data in case BlockVerboseData was requested
|
||||
if getBlocksRequest.IncludeBlockVerboseData {
|
||||
response.BlockVerboseData = make([]*appmessage.BlockVerboseData, len(blockHashes))
|
||||
response := appmessage.NewGetBlocksResponseMessage()
|
||||
response.BlockHashes = hashes.ToStrings(blockHashes)
|
||||
if getBlocksRequest.IncludeBlocks {
|
||||
rpcBlocks := make([]*appmessage.RPCBlock, len(blockHashes))
|
||||
for i, blockHash := range blockHashes {
|
||||
blockHeader, err := context.Domain.Consensus().GetBlockHeader(blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockVerboseData, err := context.BuildBlockVerboseData(blockHeader, nil,
|
||||
getBlocksRequest.IncludeTransactionVerboseData)
|
||||
block, err := context.Domain.Consensus().GetBlockEvenIfHeaderOnly(blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response.BlockVerboseData[i] = blockVerboseData
|
||||
if getBlocksRequest.IncludeTransactions {
|
||||
rpcBlocks[i] = appmessage.DomainBlockToRPCBlock(block)
|
||||
} else {
|
||||
rpcBlocks[i] = appmessage.DomainBlockToRPCBlock(&externalapi.DomainBlock{Header: block.Header})
|
||||
}
|
||||
err = context.PopulateBlockWithVerboseData(rpcBlocks[i], block.Header, nil, getBlocksRequest.IncludeTransactions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
response.Blocks = rpcBlocks
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||
"github.com/kaspanet/kaspad/app/rpc/rpchandlers"
|
||||
@@ -13,7 +15,6 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"github.com/kaspanet/kaspad/domain/miningmanager"
|
||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||
)
|
||||
@@ -26,16 +27,18 @@ func (d fakeDomain) Consensus() externalapi.Consensus { return d }
|
||||
func (d fakeDomain) MiningManager() miningmanager.MiningManager { return nil }
|
||||
|
||||
func TestHandleGetBlocks(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
stagingArea := model.NewStagingArea()
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestHandleGetBlocks")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestHandleGetBlocks")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
fakeContext := rpccontext.Context{
|
||||
Config: &config.Config{Flags: &config.Flags{NetworkFlags: config.NetworkFlags{ActiveNetParams: params}}},
|
||||
Config: &config.Config{Flags: &config.Flags{NetworkFlags: config.NetworkFlags{ActiveNetParams: &consensusConfig.Params}}},
|
||||
Domain: fakeDomain{tc},
|
||||
}
|
||||
|
||||
@@ -55,7 +58,7 @@ func TestHandleGetBlocks(t *testing.T) {
|
||||
antipast := make([]*externalapi.DomainHash, 0, len(slice))
|
||||
|
||||
for _, blockHash := range slice {
|
||||
isInPastOfPovBlock, err := tc.DAGTopologyManager().IsAncestorOf(blockHash, povBlock)
|
||||
isInPastOfPovBlock, err := tc.DAGTopologyManager().IsAncestorOf(stagingArea, blockHash, povBlock)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed doing reachability check: '%v'", err)
|
||||
}
|
||||
@@ -77,7 +80,7 @@ func TestHandleGetBlocks(t *testing.T) {
|
||||
// \ | /
|
||||
// etc.
|
||||
expectedOrder := make([]*externalapi.DomainHash, 0, 40)
|
||||
mergingBlock := params.GenesisHash
|
||||
mergingBlock := consensusConfig.GenesisHash
|
||||
for i := 0; i < 10; i++ {
|
||||
splitBlocks := make([]*externalapi.DomainHash, 0, 3)
|
||||
for j := 0; j < 3; j++ {
|
||||
@@ -87,7 +90,7 @@ func TestHandleGetBlocks(t *testing.T) {
|
||||
}
|
||||
splitBlocks = append(splitBlocks, blockHash)
|
||||
}
|
||||
sort.Sort(sort.Reverse(testutils.NewTestGhostDAGSorter(splitBlocks, tc, t)))
|
||||
sort.Sort(sort.Reverse(testutils.NewTestGhostDAGSorter(stagingArea, splitBlocks, tc, t)))
|
||||
restOfSplitBlocks, selectedParent := splitBlocks[:len(splitBlocks)-1], splitBlocks[len(splitBlocks)-1]
|
||||
expectedOrder = append(expectedOrder, selectedParent)
|
||||
expectedOrder = append(expectedOrder, restOfSplitBlocks...)
|
||||
@@ -130,13 +133,13 @@ func TestHandleGetBlocks(t *testing.T) {
|
||||
virtualSelectedParent, actualBlocks.BlockHashes)
|
||||
}
|
||||
|
||||
expectedOrder = append([]*externalapi.DomainHash{params.GenesisHash}, expectedOrder...)
|
||||
expectedOrder = append([]*externalapi.DomainHash{consensusConfig.GenesisHash}, expectedOrder...)
|
||||
actualOrder := getBlocks(nil)
|
||||
if !reflect.DeepEqual(actualOrder.BlockHashes, hashes.ToStrings(expectedOrder)) {
|
||||
t.Fatalf("TestHandleGetBlocks \nexpected: %v \nactual:\n%v", expectedOrder, actualOrder.BlockHashes)
|
||||
}
|
||||
|
||||
requestAllExplictly := getBlocks(params.GenesisHash)
|
||||
requestAllExplictly := getBlocks(consensusConfig.GenesisHash)
|
||||
if !reflect.DeepEqual(requestAllExplictly.BlockHashes, hashes.ToStrings(expectedOrder)) {
|
||||
t.Fatalf("TestHandleGetBlocks \nexpected: \n%v\n. actual:\n%v", expectedOrder, requestAllExplictly.BlockHashes)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,10 @@ import (
|
||||
|
||||
// HandleGetInfo handles the respectively named RPC command
|
||||
func HandleGetInfo(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
|
||||
response := appmessage.NewGetInfoResponseMessage(context.NetAdapter.ID().String())
|
||||
response := appmessage.NewGetInfoResponseMessage(
|
||||
context.NetAdapter.ID().String(),
|
||||
uint64(context.Domain.MiningManager().TransactionCount()),
|
||||
)
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -3,25 +3,22 @@ package rpchandlers
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
)
|
||||
|
||||
// HandleGetMempoolEntries handles the respectively named RPC command
|
||||
func HandleGetMempoolEntries(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
|
||||
|
||||
transactions := context.Domain.MiningManager().AllTransactions()
|
||||
entries := make([]*appmessage.MempoolEntry, 0, len(transactions))
|
||||
for _, tx := range transactions {
|
||||
transactionVerboseData, err := context.BuildTransactionVerboseData(
|
||||
tx, consensushashing.TransactionID(tx).String(), nil, "")
|
||||
for _, transaction := range transactions {
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
||||
err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
entries = append(entries, &appmessage.MempoolEntry{
|
||||
Fee: tx.Fee,
|
||||
TransactionVerboseData: transactionVerboseData,
|
||||
Fee: transaction.Fee,
|
||||
Transaction: rpcTransaction,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -24,12 +24,11 @@ func HandleGetMempoolEntry(context *rpccontext.Context, _ *router.Router, reques
|
||||
errorMessage.Error = appmessage.RPCErrorf("Transaction %s was not found", transactionID)
|
||||
return errorMessage, nil
|
||||
}
|
||||
|
||||
transactionVerboseData, err := context.BuildTransactionVerboseData(
|
||||
transaction, getMempoolEntryRequest.TxID, nil, "")
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
||||
err = context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return appmessage.NewGetMempoolEntryResponseMessage(transaction.Fee, transactionVerboseData), nil
|
||||
return appmessage.NewGetMempoolEntryResponseMessage(transaction.Fee, rpcTransaction), nil
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@ import (
|
||||
"github.com/kaspanet/kaspad/util/panics"
|
||||
)
|
||||
|
||||
var log, _ = logger.Get(logger.SubsystemTags.RPCS)
|
||||
var log = logger.RegisterSubSystem("RPCS")
|
||||
var spawn = panics.GoroutineWrapperFunc(log)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package rpchandlers
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
)
|
||||
|
||||
// HandleNotifyPruningPointUTXOSetOverrideRequest handles the respectively named RPC command
|
||||
func HandleNotifyPruningPointUTXOSetOverrideRequest(context *rpccontext.Context, router *router.Router, _ appmessage.Message) (appmessage.Message, error) {
|
||||
listener, err := context.NotificationManager.Listener(router)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listener.PropagatePruningPointUTXOSetOverrideNotifications()
|
||||
|
||||
response := appmessage.NewNotifyPruningPointUTXOSetOverrideResponseMessage()
|
||||
return response, nil
|
||||
}
|
||||
19
app/rpc/rpchandlers/notify_virtual_daa_score_changed.go
Normal file
19
app/rpc/rpchandlers/notify_virtual_daa_score_changed.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package rpchandlers
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
)
|
||||
|
||||
// HandleNotifyVirtualDaaScoreChanged handles the respectively named RPC command
|
||||
func HandleNotifyVirtualDaaScoreChanged(context *rpccontext.Context, router *router.Router, _ appmessage.Message) (appmessage.Message, error) {
|
||||
listener, err := context.NotificationManager.Listener(router)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listener.PropagateVirtualDaaScoreChangedNotifications()
|
||||
|
||||
response := appmessage.NewNotifyVirtualDaaScoreChangedResponseMessage()
|
||||
return response, nil
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package rpchandlers
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
)
|
||||
|
||||
// HandleStopNotifyingPruningPointUTXOSetOverrideRequest handles the respectively named RPC command
|
||||
func HandleStopNotifyingPruningPointUTXOSetOverrideRequest(context *rpccontext.Context, router *router.Router, _ appmessage.Message) (appmessage.Message, error) {
|
||||
listener, err := context.NotificationManager.Listener(router)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listener.StopPropagatingPruningPointUTXOSetOverrideNotifications()
|
||||
|
||||
response := appmessage.NewStopNotifyingPruningPointUTXOSetOverrideResponseMessage()
|
||||
return response, nil
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package rpchandlers
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
@@ -13,9 +14,6 @@ import (
|
||||
func HandleSubmitBlock(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||
submitBlockRequest := request.(*appmessage.SubmitBlockRequestMessage)
|
||||
|
||||
msgBlock := submitBlockRequest.Block
|
||||
domainBlock := appmessage.MsgBlockToDomainBlock(msgBlock)
|
||||
|
||||
if context.ProtocolManager.IsIBDRunning() {
|
||||
return &appmessage.SubmitBlockResponseMessage{
|
||||
Error: appmessage.RPCErrorf("Block not submitted - IBD is running"),
|
||||
@@ -23,11 +21,21 @@ func HandleSubmitBlock(context *rpccontext.Context, _ *router.Router, request ap
|
||||
}, nil
|
||||
}
|
||||
|
||||
err := context.ProtocolManager.AddBlock(domainBlock)
|
||||
domainBlock, err := appmessage.RPCBlockToDomainBlock(submitBlockRequest.Block)
|
||||
if err != nil {
|
||||
if !errors.As(err, &ruleerrors.RuleError{}) {
|
||||
return &appmessage.SubmitBlockResponseMessage{
|
||||
Error: appmessage.RPCErrorf("Could not parse block: %s", err),
|
||||
RejectReason: appmessage.RejectReasonBlockInvalid,
|
||||
}, nil
|
||||
}
|
||||
|
||||
err = context.ProtocolManager.AddBlock(domainBlock)
|
||||
if err != nil {
|
||||
isProtocolOrRuleError := errors.As(err, &ruleerrors.RuleError{}) || errors.As(err, &protocolerrors.ProtocolError{})
|
||||
if !isProtocolOrRuleError {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &appmessage.SubmitBlockResponseMessage{
|
||||
Error: appmessage.RPCErrorf("Block rejected. Reason: %s", err),
|
||||
RejectReason: appmessage.RejectReasonBlockInvalid,
|
||||
|
||||
@@ -16,7 +16,7 @@ func HandleUnban(context *rpccontext.Context, _ *router.Router, request appmessa
|
||||
errorMessage.Error = appmessage.RPCErrorf("Could not parse IP %s", unbanRequest.IP)
|
||||
return errorMessage, nil
|
||||
}
|
||||
err := context.AddressManager.Unban(appmessage.NewNetAddressIPPort(ip, 0, 0))
|
||||
err := context.AddressManager.Unban(appmessage.NewNetAddressIPPort(ip, 0))
|
||||
if err != nil {
|
||||
errorMessage := &appmessage.UnbanResponseMessage{}
|
||||
errorMessage.Error = appmessage.RPCErrorf("Could not unban IP: %s", err)
|
||||
|
||||
95
changelog.txt
Normal file
95
changelog.txt
Normal file
@@ -0,0 +1,95 @@
|
||||
Kaspad v0.10.2 - 2021-05-18
|
||||
===========================
|
||||
Non-breaking changes:
|
||||
* Fix getBlock and getBlocks RPC commands to return blocks and transactions properly (#1716)
|
||||
* serializeAddress should always serialize as IPv6, since it assumes the IP size is 16 bytes (#1720)
|
||||
* Add VirtualDaaScore to GetBlockDagInfo (#1719)
|
||||
* Fix calcTxSequenceLockFromReferencedUTXOEntries for loop break condition (#1723)
|
||||
* Fix overflow when checking coinbase maturity and don't ban peers that send transactions with immature spend (#1722)
|
||||
|
||||
Kaspad v0.10.1 - 2021-05-11
|
||||
===========================
|
||||
* Calculate virtual's acceptance data and multiset after importing a new pruning point (#1700)
|
||||
|
||||
Kaspad v0.10.0 - 2021-04-26
|
||||
===========================
|
||||
Major changes include:
|
||||
* Implementing a signature hashing scheme similar to BIP-143
|
||||
* Replacing HASH160 with BLAKE2B
|
||||
* Replacing ECMH with MuHash
|
||||
* Removing RIPEMD160 and SHA1 from the codebase entirely
|
||||
* Making P2PKH transactions non-standard
|
||||
* Vastly enhancing the CLI wallet
|
||||
* Restructuring kaspad's app/home directory
|
||||
* Modifying block and transaction types in the RPC to be easier to consume clientside
|
||||
|
||||
A partial list of the more-important commits is as follows:
|
||||
* Fix data race in GetBlockChildren (#1579)
|
||||
* Remove payload hash (#1583)
|
||||
* Add the mempool size to getInfo RPC command (#1584)
|
||||
* Change the difficulty to be calculated based on the same block instead of its selected parent (#1591)
|
||||
* Adjust the difficulty in the first difficultyAdjustmentWindowSize blocks (#1592)
|
||||
* Adding DAA score (#1596)
|
||||
* Use DAA score where needed (#1602)
|
||||
* Remove the Services field from NetAddress. (#1610)
|
||||
* Fix getBlocks to not add the anticone when some blocks were filtered by GetHashesBetween (#1611)
|
||||
* Restructure the default ~/.kaspad directory layout (#1613)
|
||||
* Replace the HomeDir flag with a AppDir flag (#1615)
|
||||
* Implement BIP-143-like sighash (#1598)
|
||||
* Change --datadir to --appdir and remove symmetrical connection in stability tests (#1617)
|
||||
* Use BLAKE2B instead of HASH160, and get rid of any usage of RIPEMD160 and SHA1 (#1618)
|
||||
* Replace ECMH with Muhash (#1624)
|
||||
* Add support for multiple staging areas (#1633)
|
||||
* Make sure the ghostdagDataStore cache is at least DifficultyAdjustmentBlockWindow sized (#1635)
|
||||
* Resolve each block status in it's own staging area (#1634)
|
||||
* Add mass limit to mempool (#1627)
|
||||
* In RPC, use RPCTransactions and RPCBlocks instead of TransactionMessages and BlockMessages (#1609)
|
||||
* Use go-secp256k1 v0.0.5 (#1640)
|
||||
* Add a show-address subcommand to kaspawallet (#1653)
|
||||
* Replace p2pkh with p2pk (#1650)
|
||||
* Implement importing private keys into the wallet (#1655)
|
||||
* Add dump unencrypted data sub command to the wallet (#1661)
|
||||
* Add ECDSA support (#1657)
|
||||
* Add OpCheckMultiSigECDSA (#1663)
|
||||
* Add ECDSA support to the wallet (#1664)
|
||||
* Make moving the pruning point faster (#1660)
|
||||
* Implement new mechanism for updating UTXO Diffs (#1671)
|
||||
|
||||
Kaspad v0.9.2 - 2021-03-31
|
||||
===========================
|
||||
* Increase the route capacity of InvTransaction messages. (#1603) (#1637)
|
||||
|
||||
Kaspad v0.9.1 - 2021-03-14
|
||||
===========================
|
||||
* Testnet network reset
|
||||
|
||||
Kaspad v0.9.0 - 2021-03-04
|
||||
===========================
|
||||
|
||||
* Merge big subdags in pick virtual parents (#1574)
|
||||
* Write in the reject message the tx rejection reason (#1573)
|
||||
* Add nil checks for protowire (#1570)
|
||||
* Increase getBlocks limit to 1000 (#1572)
|
||||
* Return RPC error if getBlock's lowHash doesn't exist (#1569)
|
||||
* Add default dns-seeder to testnet (#1568)
|
||||
* Fix utxoindex deserialization (#1566)
|
||||
* Add pruning point hash to GetBlockDagInfo response (#1565)
|
||||
* Use EmitUnpopulated so that kaspactl prints all fields, even the default ones (#1561)
|
||||
* Stop logging an error whenever an RPC/P2P connection is canceled (#1562)
|
||||
* Cleanup the logger and make it asynchronous (#1524)
|
||||
* Close all iterators (#1542)
|
||||
* Add childrenHashes to GetBlock/s RPC commands (#1560)
|
||||
* Add ScriptPublicKey.Version to RPC (#1559)
|
||||
* Fix the target block rate to create less bursty mining (#1554)
|
||||
|
||||
Kaspad v0.8.10 - 2021-02-25
|
||||
===========================
|
||||
|
||||
* Fix bug where invalid mempool transactions were not removed (#1551)
|
||||
* Add RPC reconnection to the miner (#1552)
|
||||
* Remove virtual diff parents - only selectedTip is virtualDiffParent now (#1550)
|
||||
* Fix UTXO index (#1548)
|
||||
* Prevent fast failing (#1545)
|
||||
* Increase the sleep time in kaspaminer when the node is not synced (#1544)
|
||||
* Disallow header only blocks on RPC, relay and when requesting IBD full blocks (#1537)
|
||||
* Make templateManager hold a DomainBlock and isSynced bool instead of a GetBlockTemplateResponseMessage (#1538)
|
||||
9
cmd/genkeypair/README.md
Normal file
9
cmd/genkeypair/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
genkeypair
|
||||
========
|
||||
|
||||
A tool for generating private-key-address pairs.
|
||||
|
||||
Note: This tool prints unencrypted private keys and is not recommended for day
|
||||
to day use, and is intended mainly for tests.
|
||||
|
||||
In order to manage your funds it's recommended to use [kaspawallet](../kaspawallet)
|
||||
26
cmd/genkeypair/config.go
Normal file
26
cmd/genkeypair/config.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/jessevdk/go-flags"
|
||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||
)
|
||||
|
||||
type configFlags struct {
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
func parseConfig() (*configFlags, error) {
|
||||
cfg := &configFlags{}
|
||||
parser := flags.NewParser(cfg, flags.PrintErrors|flags.HelpFlag)
|
||||
_, err := parser.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = cfg.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
27
cmd/genkeypair/main.go
Normal file
27
cmd/genkeypair/main.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg, err := parseConfig()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
privateKey, publicKey, err := libkaspawallet.CreateKeyPair(false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
addr, err := util.NewAddressPublicKey(publicKey, cfg.NetParams().Prefix)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("Private key: %x\n", privateKey)
|
||||
fmt.Printf("Address: %s\n", addr)
|
||||
}
|
||||
@@ -4,7 +4,7 @@ kaspactl is an RPC client for kaspad
|
||||
|
||||
## Requirements
|
||||
|
||||
Go 1.15 or later.
|
||||
Go 1.16 or later.
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -48,6 +48,10 @@ func setField(commandValue reflect.Value, parameterValue reflect.Value, paramete
|
||||
}
|
||||
|
||||
func stringToValue(parameterDesc *parameterDescription, valueStr string) (reflect.Value, error) {
|
||||
if valueStr == "-" {
|
||||
return reflect.Zero(parameterDesc.typeof), nil
|
||||
}
|
||||
|
||||
var value interface{}
|
||||
var err error
|
||||
switch parameterDesc.typeof.Kind() {
|
||||
|
||||
@@ -24,6 +24,7 @@ var commandTypes = []reflect.Type{
|
||||
reflect.TypeOf(protowire.KaspadMessage_GetVirtualSelectedParentBlueScoreRequest{}),
|
||||
reflect.TypeOf(protowire.KaspadMessage_GetVirtualSelectedParentChainFromBlockRequest{}),
|
||||
reflect.TypeOf(protowire.KaspadMessage_ResolveFinalityConflictRequest{}),
|
||||
reflect.TypeOf(protowire.KaspadMessage_EstimateNetworkHashesPerSecondRequest{}),
|
||||
|
||||
reflect.TypeOf(protowire.KaspadMessage_GetBlockTemplateRequest{}),
|
||||
reflect.TypeOf(protowire.KaspadMessage_SubmitBlockRequest{}),
|
||||
|
||||
@@ -27,7 +27,8 @@ func parseConfig() (*configFlags, error) {
|
||||
}
|
||||
parser := flags.NewParser(cfg, flags.HelpFlag)
|
||||
parser.Usage = "kaspactl [OPTIONS] [COMMAND] [COMMAND PARAMETERS].\n\nCommand can be supplied only if --json is not used." +
|
||||
"\n\nUse `kaspactl --list-commands` to get a list of all commands and their parameters"
|
||||
"\n\nUse `kaspactl --list-commands` to get a list of all commands and their parameters." +
|
||||
"\nFor optional parameters- use '-' without quotes to not pass the parameter.\n"
|
||||
remainingArgs, err := parser.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -- 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
|
||||
|
||||
|
||||
@@ -2,10 +2,11 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/server/grpcserver/protowire"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/server/grpcserver/protowire"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
|
||||
@@ -67,7 +68,7 @@ func postCommand(cfg *configFlags, client *grpcclient.GRPCClient, responseChan c
|
||||
if err != nil {
|
||||
printErrorAndExit(fmt.Sprintf("error posting the request to the RPC server: %s", err))
|
||||
}
|
||||
responseBytes, err := protojson.Marshal(response)
|
||||
responseBytes, err := protojson.MarshalOptions{EmitUnpopulated: true}.Marshal(response)
|
||||
if err != nil {
|
||||
printErrorAndExit(errors.Wrapf(err, "error parsing the response from the RPC server").Error())
|
||||
}
|
||||
@@ -92,6 +93,7 @@ func prettifyResponse(response string) string {
|
||||
|
||||
marshalOptions := &protojson.MarshalOptions{}
|
||||
marshalOptions.Indent = " "
|
||||
marshalOptions.EmitUnpopulated = true
|
||||
return marshalOptions.Format(kaspadMessage)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ Kaspaminer is a CPU-based miner for kaspad
|
||||
|
||||
## Requirements
|
||||
|
||||
Go 1.15 or later.
|
||||
Go 1.16 or later.
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -13,34 +13,47 @@ const minerTimeout = 10 * time.Second
|
||||
type minerClient struct {
|
||||
*rpcclient.RPCClient
|
||||
|
||||
cfg *configFlags
|
||||
blockAddedNotificationChan chan struct{}
|
||||
}
|
||||
|
||||
func newMinerClient(cfg *configFlags) (*minerClient, error) {
|
||||
rpcAddress, err := cfg.NetParams().NormalizeRPCServerAddress(cfg.RPCServer)
|
||||
func (mc *minerClient) connect() error {
|
||||
rpcAddress, err := mc.cfg.NetParams().NormalizeRPCServerAddress(mc.cfg.RPCServer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
rpcClient, err := rpcclient.NewRPCClient(rpcAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
rpcClient.SetTimeout(minerTimeout)
|
||||
rpcClient.SetLogger(backendLog, logger.LevelTrace)
|
||||
mc.RPCClient = rpcClient
|
||||
mc.SetTimeout(minerTimeout)
|
||||
mc.SetLogger(backendLog, logger.LevelTrace)
|
||||
|
||||
minerClient := &minerClient{
|
||||
RPCClient: rpcClient,
|
||||
blockAddedNotificationChan: make(chan struct{}),
|
||||
}
|
||||
|
||||
err = rpcClient.RegisterForBlockAddedNotifications(func(_ *appmessage.BlockAddedNotificationMessage) {
|
||||
err = mc.RegisterForBlockAddedNotifications(func(_ *appmessage.BlockAddedNotificationMessage) {
|
||||
select {
|
||||
case minerClient.blockAddedNotificationChan <- struct{}{}:
|
||||
case mc.blockAddedNotificationChan <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error requesting block-added notifications")
|
||||
return errors.Wrapf(err, "error requesting block-added notifications")
|
||||
}
|
||||
|
||||
log.Infof("Connected to %s", rpcAddress)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newMinerClient(cfg *configFlags) (*minerClient, error) {
|
||||
minerClient := &minerClient{
|
||||
cfg: cfg,
|
||||
blockAddedNotificationChan: make(chan struct{}),
|
||||
}
|
||||
|
||||
err := minerClient.connect()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return minerClient, nil
|
||||
|
||||
@@ -24,9 +24,9 @@ const (
|
||||
|
||||
var (
|
||||
// Default configuration options
|
||||
defaultHomeDir = util.AppDataDir("kaspaminer", false)
|
||||
defaultLogFile = filepath.Join(defaultHomeDir, defaultLogFilename)
|
||||
defaultErrLogFile = filepath.Join(defaultHomeDir, defaultErrLogFilename)
|
||||
defaultAppDir = util.AppDir("kaspaminer", false)
|
||||
defaultLogFile = filepath.Join(defaultAppDir, defaultLogFilename)
|
||||
defaultErrLogFile = filepath.Join(defaultAppDir, defaultErrLogFilename)
|
||||
defaultRPCServer = "localhost"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -- 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
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ var (
|
||||
)
|
||||
|
||||
func initLog(logFile, errLogFile string) {
|
||||
log.SetLevel(logger.LevelDebug)
|
||||
err := backendLog.AddLogFile(logFile, logger.LevelTrace)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error adding log file %s as log rotator for level %s: %s", logFile, logger.LevelTrace, err)
|
||||
@@ -24,4 +25,15 @@ func initLog(logFile, errLogFile string) {
|
||||
fmt.Fprintf(os.Stderr, "Error adding log file %s as log rotator for level %s: %s", errLogFile, logger.LevelWarn, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = backendLog.AddLogWriter(os.Stdout, logger.LevelInfo)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error adding stdout to the loggerfor level %s: %s", logger.LevelWarn, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = backendLog.Run()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error starting the logger: %s ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ func main() {
|
||||
fmt.Fprintf(os.Stderr, "Error parsing command-line arguments: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer backendLog.Close()
|
||||
|
||||
// Show version at startup.
|
||||
log.Infof("Version %s", version.Version())
|
||||
|
||||
@@ -2,21 +2,18 @@ package main
|
||||
|
||||
import (
|
||||
nativeerrors "errors"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspaminer/templatemanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/pow"
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
"math/rand"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspaminer/templatemanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/pow"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -43,32 +40,33 @@ func mineLoop(client *minerClient, numberOfBlocks uint64, targetBlocksPerSecond
|
||||
|
||||
spawn("blocksLoop", func() {
|
||||
const windowSize = 10
|
||||
var expectedDurationForWindow time.Duration
|
||||
var windowExpectedEndTime time.Time
|
||||
hasBlockRateTarget := targetBlocksPerSecond != 0
|
||||
var windowTicker, blockTicker *time.Ticker
|
||||
// We use tickers to limit the block rate:
|
||||
// 1. windowTicker -> makes sure that the last windowSize blocks take at least windowSize*targetBlocksPerSecond.
|
||||
// 2. blockTicker -> makes sure that each block takes at least targetBlocksPerSecond/windowSize.
|
||||
// that way we both allow for fluctuation in block rate but also make sure they're not too big (by an order of magnitude)
|
||||
if hasBlockRateTarget {
|
||||
expectedDurationForWindow = time.Duration(float64(windowSize)/targetBlocksPerSecond) * time.Second
|
||||
windowExpectedEndTime = time.Now().Add(expectedDurationForWindow)
|
||||
windowRate := time.Duration(float64(time.Second) / (targetBlocksPerSecond / windowSize))
|
||||
blockRate := time.Duration(float64(time.Second) / (targetBlocksPerSecond * windowSize))
|
||||
log.Infof("Minimum average time per %d blocks: %s, smaller minimum time per block: %s", windowSize, windowRate, blockRate)
|
||||
windowTicker = time.NewTicker(windowRate)
|
||||
blockTicker = time.NewTicker(blockRate)
|
||||
defer windowTicker.Stop()
|
||||
defer blockTicker.Stop()
|
||||
}
|
||||
blockInWindowIndex := 0
|
||||
|
||||
for {
|
||||
windowStart := time.Now()
|
||||
for blockIndex := 1; ; blockIndex++ {
|
||||
foundBlockChan <- mineNextBlock(mineWhenNotSynced)
|
||||
|
||||
if hasBlockRateTarget {
|
||||
blockInWindowIndex++
|
||||
if blockInWindowIndex == windowSize-1 {
|
||||
deviation := windowExpectedEndTime.Sub(time.Now())
|
||||
if deviation > 0 {
|
||||
log.Infof("Finished to mine %d blocks %s earlier than expected. Sleeping %s to compensate",
|
||||
windowSize, deviation, deviation)
|
||||
time.Sleep(deviation)
|
||||
}
|
||||
blockInWindowIndex = 0
|
||||
windowExpectedEndTime = time.Now().Add(expectedDurationForWindow)
|
||||
<-blockTicker.C
|
||||
if (blockIndex % windowSize) == 0 {
|
||||
tickerStart := time.Now()
|
||||
<-windowTicker.C
|
||||
log.Infof("Finished mining %d blocks in: %s. slept for: %s", windowSize, time.Since(windowStart), time.Since(tickerStart))
|
||||
windowStart = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
@@ -118,6 +116,11 @@ func handleFoundBlock(client *minerClient, block *externalapi.DomainBlock) error
|
||||
if err != nil {
|
||||
if nativeerrors.Is(err, router.ErrTimeout) {
|
||||
log.Warnf("Got timeout while submitting block %s to %s: %s", blockHash, client.Address(), err)
|
||||
return client.Reconnect()
|
||||
}
|
||||
if nativeerrors.Is(err, router.ErrRouteClosed) {
|
||||
log.Debugf("Got route is closed while requesting block template from %s. "+
|
||||
"The client is most likely reconnecting", client.Address())
|
||||
return nil
|
||||
}
|
||||
if rejectReason == appmessage.RejectReasonIsInIBD {
|
||||
@@ -126,7 +129,7 @@ func handleFoundBlock(client *minerClient, block *externalapi.DomainBlock) error
|
||||
time.Sleep(waitTime)
|
||||
return nil
|
||||
}
|
||||
return errors.Errorf("Error submitting block %s to %s: %s", blockHash, client.Address(), err)
|
||||
return errors.Wrapf(err, "Error submitting block %s to %s", blockHash, client.Address())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -155,11 +158,15 @@ func mineNextBlock(mineWhenNotSynced bool) *externalapi.DomainBlock {
|
||||
|
||||
func getBlockForMining(mineWhenNotSynced bool) *externalapi.DomainBlock {
|
||||
tryCount := 0
|
||||
|
||||
const sleepTime = 500 * time.Millisecond
|
||||
const sleepTimeWhenNotSynced = 5 * time.Second
|
||||
|
||||
for {
|
||||
tryCount++
|
||||
const sleepTime = 500 * time.Millisecond
|
||||
|
||||
shouldLog := (tryCount-1)%10 == 0
|
||||
template := templatemanager.Get()
|
||||
template, isSynced := templatemanager.Get()
|
||||
if template == nil {
|
||||
if shouldLog {
|
||||
log.Info("Waiting for the initial template")
|
||||
@@ -167,15 +174,15 @@ func getBlockForMining(mineWhenNotSynced bool) *externalapi.DomainBlock {
|
||||
time.Sleep(sleepTime)
|
||||
continue
|
||||
}
|
||||
if !template.IsSynced && !mineWhenNotSynced {
|
||||
if !isSynced && !mineWhenNotSynced {
|
||||
if shouldLog {
|
||||
log.Warnf("Kaspad is not synced. Skipping current block template")
|
||||
}
|
||||
time.Sleep(sleepTime)
|
||||
time.Sleep(sleepTimeWhenNotSynced)
|
||||
continue
|
||||
}
|
||||
|
||||
return appmessage.MsgBlockToDomainBlock(template.MsgBlock)
|
||||
return template
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,13 +191,26 @@ func templatesLoop(client *minerClient, miningAddr util.Address, errChan chan er
|
||||
template, err := client.GetBlockTemplate(miningAddr.String())
|
||||
if nativeerrors.Is(err, router.ErrTimeout) {
|
||||
log.Warnf("Got timeout while requesting block template from %s: %s", client.Address(), err)
|
||||
reconnectErr := client.Reconnect()
|
||||
if reconnectErr != nil {
|
||||
errChan <- reconnectErr
|
||||
}
|
||||
return
|
||||
}
|
||||
if nativeerrors.Is(err, router.ErrRouteClosed) {
|
||||
log.Debugf("Got route is closed while requesting block template from %s. "+
|
||||
"The client is most likely reconnecting", client.Address())
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
errChan <- errors.Errorf("Error getting block template from %s: %s", client.Address(), err)
|
||||
errChan <- errors.Wrapf(err, "Error getting block template from %s", client.Address())
|
||||
return
|
||||
}
|
||||
err = templatemanager.Set(template)
|
||||
if err != nil {
|
||||
errChan <- errors.Wrapf(err, "Error setting block template from %s", client.Address())
|
||||
return
|
||||
}
|
||||
templatemanager.Set(template)
|
||||
}
|
||||
|
||||
getBlockTemplate()
|
||||
|
||||
@@ -2,22 +2,35 @@ package templatemanager
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var currentTemplate *appmessage.GetBlockTemplateResponseMessage
|
||||
var currentTemplate *externalapi.DomainBlock
|
||||
var isSynced bool
|
||||
var lock = &sync.Mutex{}
|
||||
|
||||
// Get returns the template to work on
|
||||
func Get() *appmessage.GetBlockTemplateResponseMessage {
|
||||
func Get() (*externalapi.DomainBlock, bool) {
|
||||
lock.Lock()
|
||||
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
|
||||
func Set(template *appmessage.GetBlockTemplateResponseMessage) {
|
||||
func Set(template *appmessage.GetBlockTemplateResponseMessage) error {
|
||||
block, err := appmessage.RPCBlockToDomainBlock(template.Block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
currentTemplate = template
|
||||
currentTemplate = block
|
||||
isSynced = template.IsSynced
|
||||
return nil
|
||||
}
|
||||
|
||||
31
cmd/kaspawallet/balance.go
Normal file
31
cmd/kaspawallet/balance.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/client"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
)
|
||||
|
||||
func balance(conf *balanceConfig) error {
|
||||
daemonClient, tearDown, err := client.Connect(conf.DaemonAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), daemonTimeout)
|
||||
defer cancel()
|
||||
response, err := daemonClient.GetBalance(ctx, &pb.GetBalanceRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Balance:\t\tKAS %f\n", float64(response.Available)/util.SompiPerKaspa)
|
||||
if response.Pending > 0 {
|
||||
fmt.Printf("Pending balance:\tKAS %f\n", float64(response.Pending)/util.SompiPerKaspa)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
35
cmd/kaspawallet/broadcast.go
Normal file
35
cmd/kaspawallet/broadcast.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/client"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
|
||||
)
|
||||
|
||||
func broadcast(conf *broadcastConfig) error {
|
||||
daemonClient, tearDown, err := client.Connect(conf.DaemonAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), daemonTimeout)
|
||||
defer cancel()
|
||||
|
||||
transaction, err := hex.DecodeString(conf.Transaction)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
response, err := daemonClient.Broadcast(ctx, &pb.BroadcastRequest{Transaction: transaction})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Transaction was sent successfully")
|
||||
fmt.Printf("Transaction ID: \t%s\n", response.TxID)
|
||||
|
||||
return nil
|
||||
}
|
||||
14
cmd/kaspawallet/common.go
Normal file
14
cmd/kaspawallet/common.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
const daemonTimeout = 2 * time.Minute
|
||||
|
||||
func printErrorAndExit(err error) {
|
||||
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
229
cmd/kaspawallet/config.go
Normal file
229
cmd/kaspawallet/config.go
Normal file
@@ -0,0 +1,229 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||
"github.com/pkg/errors"
|
||||
"os"
|
||||
|
||||
"github.com/jessevdk/go-flags"
|
||||
)
|
||||
|
||||
const (
|
||||
createSubCmd = "create"
|
||||
balanceSubCmd = "balance"
|
||||
sendSubCmd = "send"
|
||||
createUnsignedTransactionSubCmd = "create-unsigned-transaction"
|
||||
signSubCmd = "sign"
|
||||
broadcastSubCmd = "broadcast"
|
||||
showAddressSubCmd = "show-address"
|
||||
dumpUnencryptedDataSubCmd = "dump-unencrypted-data"
|
||||
startDaemonSubCmd = "start-daemon"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultListen = "localhost:8082"
|
||||
defaultRPCServer = "localhost"
|
||||
)
|
||||
|
||||
type configFlags struct {
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type createConfig struct {
|
||||
KeysFile string `long:"keys-file" short:"f" description:"Keys file location (default: ~/.kaspawallet/keys.json (*nix), %USERPROFILE%\\AppData\\Local\\Kaspawallet\\key.json (Windows))"`
|
||||
Password string `long:"password" short:"p" description:"Wallet password"`
|
||||
Yes bool `long:"yes" short:"y" description:"Assume \"yes\" to all questions"`
|
||||
MinimumSignatures uint32 `long:"min-signatures" short:"m" description:"Minimum required signatures" default:"1"`
|
||||
NumPrivateKeys uint32 `long:"num-private-keys" short:"k" description:"Number of private keys" default:"1"`
|
||||
NumPublicKeys uint32 `long:"num-public-keys" short:"n" description:"Total number of keys" default:"1"`
|
||||
ECDSA bool `long:"ecdsa" description:"Create an ECDSA wallet"`
|
||||
Import bool `long:"import" short:"i" description:"Import private keys (as opposed to generating them)"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type balanceConfig struct {
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to (default: localhost:8082)"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type sendConfig struct {
|
||||
KeysFile string `long:"keys-file" short:"f" description:"Keys file location (default: ~/.kaspawallet/keys.json (*nix), %USERPROFILE%\\AppData\\Local\\Kaspawallet\\key.json (Windows))"`
|
||||
Password string `long:"password" short:"p" description:"Wallet password"`
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to (default: localhost:8082)"`
|
||||
ToAddress string `long:"to-address" short:"t" description:"The public address to send Kaspa to" required:"true"`
|
||||
SendAmount float64 `long:"send-amount" short:"v" description:"An amount to send in Kaspa (e.g. 1234.12345678)" required:"true"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type createUnsignedTransactionConfig struct {
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to (default: localhost:8082)"`
|
||||
ToAddress string `long:"to-address" short:"t" description:"The public address to send Kaspa to" required:"true"`
|
||||
SendAmount float64 `long:"send-amount" short:"v" description:"An amount to send in Kaspa (e.g. 1234.12345678)" required:"true"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type signConfig struct {
|
||||
KeysFile string `long:"keys-file" short:"f" description:"Keys file location (default: ~/.kaspawallet/keys.json (*nix), %USERPROFILE%\\AppData\\Local\\Kaspawallet\\key.json (Windows))"`
|
||||
Password string `long:"password" short:"p" description:"Wallet password"`
|
||||
Transaction string `long:"transaction" short:"t" description:"The unsigned transaction to sign on (encoded in hex)" required:"true"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type broadcastConfig struct {
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to (default: localhost:8082)"`
|
||||
Transaction string `long:"transaction" short:"t" description:"The signed transaction to broadcast (encoded in hex)" required:"true"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type showAddressConfig struct {
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to (default: localhost:8082)"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type startDaemonConfig struct {
|
||||
KeysFile string `long:"keys-file" short:"f" description:"Keys file location (default: ~/.kaspawallet/keys.json (*nix), %USERPROFILE%\\AppData\\Local\\Kaspawallet\\key.json (Windows))"`
|
||||
Password string `long:"password" short:"p" description:"Wallet password"`
|
||||
RPCServer string `long:"rpcserver" short:"s" description:"RPC server to connect to"`
|
||||
Listen string `short:"l" long:"listen" description:"Address to listen on (default: 0.0.0.0:8082)"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type dumpUnencryptedDataConfig struct {
|
||||
KeysFile string `long:"keys-file" short:"f" description:"Keys file location (default: ~/.kaspawallet/keys.json (*nix), %USERPROFILE%\\AppData\\Local\\Kaspawallet\\key.json (Windows))"`
|
||||
Password string `long:"password" short:"p" description:"Wallet password"`
|
||||
Yes bool `long:"yes" short:"y" description:"Assume \"yes\" to all questions"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
func parseCommandLine() (subCommand string, config interface{}) {
|
||||
cfg := &configFlags{}
|
||||
parser := flags.NewParser(cfg, flags.PrintErrors|flags.HelpFlag)
|
||||
|
||||
createConf := &createConfig{}
|
||||
parser.AddCommand(createSubCmd, "Creates a new wallet",
|
||||
"Creates a private key and 3 public addresses, one for each of MainNet, TestNet and DevNet", createConf)
|
||||
|
||||
balanceConf := &balanceConfig{DaemonAddress: defaultListen}
|
||||
parser.AddCommand(balanceSubCmd, "Shows the balance of a public address",
|
||||
"Shows the balance for a public address in Kaspa", balanceConf)
|
||||
|
||||
sendConf := &sendConfig{DaemonAddress: defaultListen}
|
||||
parser.AddCommand(sendSubCmd, "Sends a Kaspa transaction to a public address",
|
||||
"Sends a Kaspa transaction to a public address", sendConf)
|
||||
|
||||
createUnsignedTransactionConf := &createUnsignedTransactionConfig{DaemonAddress: defaultListen}
|
||||
parser.AddCommand(createUnsignedTransactionSubCmd, "Create an unsigned Kaspa transaction",
|
||||
"Create an unsigned Kaspa transaction", createUnsignedTransactionConf)
|
||||
|
||||
signConf := &signConfig{}
|
||||
parser.AddCommand(signSubCmd, "Sign the given partially signed transaction",
|
||||
"Sign the given partially signed transaction", signConf)
|
||||
|
||||
broadcastConf := &broadcastConfig{DaemonAddress: defaultListen}
|
||||
parser.AddCommand(broadcastSubCmd, "Broadcast the given transaction",
|
||||
"Broadcast the given transaction", broadcastConf)
|
||||
|
||||
showAddressConf := &showAddressConfig{DaemonAddress: defaultListen}
|
||||
parser.AddCommand(showAddressSubCmd, "Shows the public address of the current wallet",
|
||||
"Shows the public address of the current wallet", showAddressConf)
|
||||
|
||||
dumpUnencryptedDataConf := &dumpUnencryptedDataConfig{}
|
||||
parser.AddCommand(dumpUnencryptedDataSubCmd, "Prints the unencrypted wallet data",
|
||||
"Prints the unencrypted wallet data including its private keys. Anyone that sees it can access "+
|
||||
"the funds. Use only on safe environment.", dumpUnencryptedDataConf)
|
||||
|
||||
startDaemonConf := &startDaemonConfig{
|
||||
RPCServer: defaultRPCServer,
|
||||
Listen: defaultListen,
|
||||
}
|
||||
parser.AddCommand(startDaemonSubCmd, "Start the wallet daemon", "Start the wallet daemon", startDaemonConf)
|
||||
|
||||
_, err := parser.Parse()
|
||||
|
||||
if err != nil {
|
||||
var flagsErr *flags.Error
|
||||
if ok := errors.As(err, &flagsErr); ok && flagsErr.Type == flags.ErrHelp {
|
||||
os.Exit(0)
|
||||
} else {
|
||||
os.Exit(1)
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
switch parser.Command.Active.Name {
|
||||
case createSubCmd:
|
||||
combineNetworkFlags(&createConf.NetworkFlags, &cfg.NetworkFlags)
|
||||
err := createConf.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
config = createConf
|
||||
case balanceSubCmd:
|
||||
combineNetworkFlags(&balanceConf.NetworkFlags, &cfg.NetworkFlags)
|
||||
err := balanceConf.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
config = balanceConf
|
||||
case sendSubCmd:
|
||||
combineNetworkFlags(&sendConf.NetworkFlags, &cfg.NetworkFlags)
|
||||
err := sendConf.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
config = sendConf
|
||||
case createUnsignedTransactionSubCmd:
|
||||
combineNetworkFlags(&createUnsignedTransactionConf.NetworkFlags, &cfg.NetworkFlags)
|
||||
err := createUnsignedTransactionConf.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
config = createUnsignedTransactionConf
|
||||
case signSubCmd:
|
||||
combineNetworkFlags(&signConf.NetworkFlags, &cfg.NetworkFlags)
|
||||
err := signConf.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
config = signConf
|
||||
case broadcastSubCmd:
|
||||
combineNetworkFlags(&broadcastConf.NetworkFlags, &cfg.NetworkFlags)
|
||||
err := broadcastConf.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
config = broadcastConf
|
||||
case showAddressSubCmd:
|
||||
combineNetworkFlags(&showAddressConf.NetworkFlags, &cfg.NetworkFlags)
|
||||
err := showAddressConf.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
config = showAddressConf
|
||||
case dumpUnencryptedDataSubCmd:
|
||||
combineNetworkFlags(&dumpUnencryptedDataConf.NetworkFlags, &cfg.NetworkFlags)
|
||||
err := dumpUnencryptedDataConf.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
config = dumpUnencryptedDataConf
|
||||
case startDaemonSubCmd:
|
||||
combineNetworkFlags(&startDaemonConf.NetworkFlags, &cfg.NetworkFlags)
|
||||
err := startDaemonConf.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
config = startDaemonConf
|
||||
}
|
||||
|
||||
return parser.Command.Active.Name, config
|
||||
}
|
||||
|
||||
func combineNetworkFlags(dst, src *config.NetworkFlags) {
|
||||
dst.Testnet = dst.Testnet || src.Testnet
|
||||
dst.Simnet = dst.Simnet || src.Simnet
|
||||
dst.Devnet = dst.Devnet || src.Devnet
|
||||
if dst.OverrideDAGParamsFile == "" {
|
||||
dst.OverrideDAGParamsFile = src.OverrideDAGParamsFile
|
||||
}
|
||||
}
|
||||
78
cmd/kaspawallet/create.go
Normal file
78
cmd/kaspawallet/create.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet/bip32"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/utils"
|
||||
"github.com/pkg/errors"
|
||||
"os"
|
||||
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/keys"
|
||||
)
|
||||
|
||||
func create(conf *createConfig) error {
|
||||
var encryptedMnemonics []*keys.EncryptedMnemonic
|
||||
var signerExtendedPublicKeys []string
|
||||
var err error
|
||||
isMultisig := conf.NumPublicKeys > 1
|
||||
if !conf.Import {
|
||||
encryptedMnemonics, signerExtendedPublicKeys, err = keys.CreateMnemonics(conf.NetParams(), conf.NumPrivateKeys, conf.Password, isMultisig)
|
||||
} else {
|
||||
encryptedMnemonics, signerExtendedPublicKeys, err = keys.ImportMnemonics(conf.NetParams(), conf.NumPrivateKeys, conf.Password, isMultisig)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, extendedPublicKey := range signerExtendedPublicKeys {
|
||||
fmt.Printf("Extended public key of mnemonic #%d:\n%s\n\n", i+1, extendedPublicKey)
|
||||
}
|
||||
|
||||
extendedPublicKeys := make([]string, conf.NumPrivateKeys, conf.NumPublicKeys)
|
||||
copy(extendedPublicKeys, signerExtendedPublicKeys)
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
for i := conf.NumPrivateKeys; i < conf.NumPublicKeys; i++ {
|
||||
fmt.Printf("Enter public key #%d here:\n", i+1)
|
||||
extendedPublicKey, err := utils.ReadLine(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = bip32.DeserializeExtendedKey(string(extendedPublicKey))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "%s is invalid extended public key", string(extendedPublicKey))
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
|
||||
extendedPublicKeys = append(extendedPublicKeys, string(extendedPublicKey))
|
||||
}
|
||||
|
||||
cosignerIndex, err := libkaspawallet.MinimumCosignerIndex(signerExtendedPublicKeys, extendedPublicKeys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file := keys.File{
|
||||
EncryptedMnemonics: encryptedMnemonics,
|
||||
ExtendedPublicKeys: extendedPublicKeys,
|
||||
MinimumSignatures: conf.MinimumSignatures,
|
||||
CosignerIndex: cosignerIndex,
|
||||
ECDSA: conf.ECDSA,
|
||||
}
|
||||
|
||||
err = file.SetPath(conf.NetParams(), conf.KeysFile, conf.Yes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = file.Save()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Wrote the keys into %s\n", file.Path())
|
||||
return nil
|
||||
}
|
||||
34
cmd/kaspawallet/create_unsigned_tx.go
Normal file
34
cmd/kaspawallet/create_unsigned_tx.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/client"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
)
|
||||
|
||||
func createUnsignedTransaction(conf *createUnsignedTransactionConfig) error {
|
||||
daemonClient, tearDown, err := client.Connect(conf.DaemonAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), daemonTimeout)
|
||||
defer cancel()
|
||||
|
||||
sendAmountSompi := uint64(conf.SendAmount * util.SompiPerKaspa)
|
||||
response, err := daemonClient.CreateUnsignedTransaction(ctx, &pb.CreateUnsignedTransactionRequest{
|
||||
Address: conf.ToAddress,
|
||||
Amount: sendAmountSompi,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Created unsigned transaction")
|
||||
fmt.Println(hex.EncodeToString(response.UnsignedTransaction))
|
||||
return nil
|
||||
}
|
||||
18
cmd/kaspawallet/daemon/client/client.go
Normal file
18
cmd/kaspawallet/daemon/client/client.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Connect connects to the kaspawalletd server, and returns the client instance
|
||||
func Connect(address string) (pb.KaspawalletdClient, func(), error) {
|
||||
conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return pb.NewKaspawalletdClient(conn), func() {
|
||||
conn.Close()
|
||||
}, nil
|
||||
}
|
||||
3
cmd/kaspawallet/daemon/pb/generate.go
Normal file
3
cmd/kaspawallet/daemon/pb/generate.go
Normal file
@@ -0,0 +1,3 @@
|
||||
//go:generate protoc --go_out=. --go-grpc_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative kaspawalletd.proto
|
||||
|
||||
package pb
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user