Compare commits

..

6 Commits

Author SHA1 Message Date
Elichai Turkel
c198a3839e Fixed DAGToplogy test mock 2021-03-02 20:28:52 +02:00
Elichai Turkel
2cf101de6a Add comments and fix off-by-one in the mergeSetIncrease queue 2021-03-02 20:28:52 +02:00
Elichai Turkel
42ec173726 Check if the new candidate is in the future of any existing candidate 2021-03-02 20:28:52 +02:00
Elichai Turkel
f9ea805fee Add new IsAnyAncestorOf to DagTopolyManager 2021-03-02 20:28:52 +02:00
Elichai Turkel
da5667f84d Remove unneeded Heap/HashSet usages 2021-03-02 20:28:51 +02:00
Elichai Turkel
d2ad34e887 Refactor mergeSetIncrease to return the current BFS block to allow easier merging 2021-03-02 20:28:49 +02:00
571 changed files with 10185 additions and 26685 deletions

View File

@@ -1,77 +0,0 @@
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

View File

@@ -13,7 +13,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ ubuntu-16.04, macos-10.15, windows-2019 ]
os: [ ubuntu-16.04, macos-10.15 ]
name: Testing on on ${{ matrix.os }}
steps:
@@ -63,9 +63,6 @@ jobs:
with:
go-version: 1.16
- name: Delete the stability tests from coverage
run: rm -r stability-tests
- name: Create coverage file
run: go test -v -covermode=atomic -coverpkg=./... -coverprofile coverage.txt ./...

View File

@@ -56,15 +56,13 @@ $ kaspad
```
## Discord
Join our discord server using the following link: https://discord.gg/YNYnNN5Pf2
Join our discord server using the following link: https://discord.gg/WmGhhzk
## 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

View File

@@ -85,6 +85,12 @@ 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
@@ -157,9 +163,15 @@ 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.AppDir, "data")
return filepath.Join(cfg.DataDir, "db")
}
func removeDatabase(cfg *config.Config) error {
@@ -169,17 +181,6 @@ 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)
db, err := ldb.NewLevelDB(dbPath, leveldbCacheSizeMiB)
if err != nil {
return nil, err
}
return db, nil
return ldb.NewLevelDB(dbPath, leveldbCacheSizeMiB)
}

View File

@@ -3,7 +3,6 @@ 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"
@@ -84,6 +83,7 @@ func DomainTransactionToMsgTx(domainTransaction *externalapi.DomainTransaction)
LockTime: domainTransaction.LockTime,
SubnetworkID: domainTransaction.SubnetworkID,
Gas: domainTransaction.Gas,
PayloadHash: domainTransaction.PayloadHash,
Payload: domainTransaction.Payload,
}
}
@@ -133,6 +133,7 @@ func MsgTxToDomainTransaction(msgTx *MsgTx) *externalapi.DomainTransaction {
LockTime: msgTx.LockTime,
SubnetworkID: msgTx.SubnetworkID,
Gas: msgTx.Gas,
PayloadHash: msgTx.PayloadHash,
Payload: payload,
}
}
@@ -163,10 +164,14 @@ 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 {
previousOutpoint, err := RPCOutpointToDomainOutpoint(input.PreviousOutpoint)
transactionID, err := transactionid.FromString(input.PreviousOutpoint.TransactionID)
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
@@ -193,6 +198,10 @@ func RPCTransactionToDomainTransaction(rpcTransaction *RPCTransaction) (*externa
if err != nil {
return nil, err
}
payloadHash, err := externalapi.NewDomainHashFromString(rpcTransaction.PayloadHash)
if err != nil {
return nil, err
}
payload, err := hex.DecodeString(rpcTransaction.Payload)
if err != nil {
return nil, err
@@ -205,40 +214,11 @@ 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))
@@ -264,6 +244,7 @@ func DomainTransactionToRPCTransaction(transaction *externalapi.DomainTransactio
}
}
subnetworkID := transaction.SubnetworkID.String()
payloadHash := transaction.PayloadHash.String()
payload := hex.EncodeToString(transaction.Payload)
return &RPCTransaction{
Version: transaction.Version,
@@ -272,6 +253,7 @@ func DomainTransactionToRPCTransaction(transaction *externalapi.DomainTransactio
LockTime: transaction.LockTime,
SubnetworkID: subnetworkID,
Gas: transaction.LockTime,
PayloadHash: payloadHash,
Payload: payload,
}
}
@@ -283,27 +265,22 @@ func OutpointAndUTXOEntryPairsToDomainOutpointAndUTXOEntryPairs(
domainOutpointAndUTXOEntryPairs := make([]*externalapi.OutpointAndUTXOEntryPair, len(outpointAndUTXOEntryPairs))
for i, outpointAndUTXOEntryPair := range outpointAndUTXOEntryPairs {
domainOutpointAndUTXOEntryPairs[i] = outpointAndUTXOEntryPairToDomainOutpointAndUTXOEntryPair(outpointAndUTXOEntryPair)
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,
),
}
}
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(
@@ -320,76 +297,9 @@ func DomainOutpointAndUTXOEntryPairsToOutpointAndUTXOEntryPairs(
Amount: outpointAndUTXOEntryPair.UTXOEntry.Amount(),
ScriptPublicKey: outpointAndUTXOEntryPair.UTXOEntry.ScriptPublicKey(),
IsCoinbase: outpointAndUTXOEntryPair.UTXOEntry.IsCoinbase(),
BlockDAAScore: outpointAndUTXOEntryPair.UTXOEntry.BlockDAAScore(),
BlockBlueScore: outpointAndUTXOEntryPair.UTXOEntry.BlockBlueScore(),
},
}
}
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
}

View File

@@ -137,11 +137,6 @@ const (
CmdPruningPointUTXOSetOverrideNotificationMessage
CmdStopNotifyingPruningPointUTXOSetOverrideRequestMessage
CmdStopNotifyingPruningPointUTXOSetOverrideResponseMessage
CmdEstimateNetworkHashesPerSecondRequestMessage
CmdEstimateNetworkHashesPerSecondResponseMessage
CmdNotifyVirtualDaaScoreChangedRequestMessage
CmdNotifyVirtualDaaScoreChangedResponseMessage
CmdVirtualDaaScoreChangedNotificationMessage
)
// ProtocolMessageCommandToString maps all MessageCommands to their string representation
@@ -253,11 +248,6 @@ var RPCMessageCommandToString = map[MessageCommand]string{
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

View File

@@ -15,6 +15,19 @@ 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 {

View File

@@ -31,6 +31,6 @@ type OutpointAndUTXOEntryPair struct {
type UTXOEntry struct {
Amount uint64
ScriptPublicKey *externalapi.ScriptPublicKey
BlockDAAScore uint64
BlockBlueScore uint64
IsCoinbase bool
}

View File

@@ -6,6 +6,7 @@ package appmessage
import (
"encoding/binary"
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
"strconv"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
@@ -132,6 +133,7 @@ type MsgTx struct {
LockTime uint64
SubnetworkID externalapi.DomainSubnetworkID
Gas uint64
PayloadHash externalapi.DomainHash
Payload []byte
}
@@ -177,6 +179,7 @@ func (msg *MsgTx) Copy() *MsgTx {
LockTime: msg.LockTime,
SubnetworkID: msg.SubnetworkID,
Gas: msg.Gas,
PayloadHash: msg.PayloadHash,
}
if msg.Payload != nil {
@@ -277,12 +280,18 @@ 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,
}

View File

@@ -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 := "93663e597f6c968d32d229002f76408edf30d6a0151ff679fc729812d8cb2acc"
txID1Str := "24079c6d2bdf602fc389cc307349054937744a9c8dc0f07c023e6af0e949a4e7"
txHash1Str := "4bee9ee495bd93a755de428376bd582a2bb6ec37c041753b711c0606d5745c13"
txID1Str := "f868bd20e816256b80eac976821be4589d24d21141bd1cec6e8005d0c16c6881"
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 := "8dafd1bec24527d8e3b443ceb0a3b92fffc0d60026317f890b2faf5e9afc177a"
hash2Str := "cb1bdb4a83d4885535fb3cceb5c96597b7df903db83f0ffcd779d703affd8efd"
wantHash2, err := externalapi.NewDomainHashFromString(hash2Str)
if err != nil {
t.Errorf("NewTxIDFromStr: %v", err)
return
}
id2Str := "89ffb49474637502d9059af38b8a95fc2f0d3baef5c801d7a9b9c8830671b711"
id2Str := "ca080073d4ddf5b84443a0964af633f3c70a5b290fd3bc35a7e6f93fd33f9330"
wantID2, err := transactionid.FromString(id2Str)
if err != nil {
t.Errorf("NewTxIDFromStr: %v", err)

View File

@@ -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)
me := NewNetAddress(tcpAddrMe, SFNodeNetwork)
generatedID, err := id.GenerateID()
if err != nil {
t.Fatalf("id.GenerateID: %s", err)

View File

@@ -15,6 +15,9 @@ 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
@@ -23,6 +26,17 @@ 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{
@@ -33,19 +47,20 @@ 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) *NetAddress {
return NewNetAddressTimestamp(mstime.Now(), ip, port)
func NewNetAddressIPPort(ip net.IP, port uint16, services ServiceFlag) *NetAddress {
return NewNetAddressTimestamp(mstime.Now(), services, 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, ip net.IP, port uint16) *NetAddress {
timestamp mstime.Time, services ServiceFlag, 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,
}
@@ -54,6 +69,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) *NetAddress {
return NewNetAddressIPPort(addr.IP, uint16(addr.Port))
func NewNetAddress(addr *net.TCPAddr, services ServiceFlag) *NetAddress {
return NewNetAddressIPPort(addr.IP, uint16(addr.Port), services)
}

View File

@@ -15,7 +15,7 @@ func TestNetAddress(t *testing.T) {
port := 16111
// Test NewNetAddress.
na := NewNetAddress(&net.TCPAddr{IP: ip, Port: port})
na := NewNetAddress(&net.TCPAddr{IP: ip, Port: port}, 0)
// Ensure we get the same ip, port, and services back out.
if !na.IP.Equal(ip) {
@@ -25,4 +25,21 @@ 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")
}
}

View File

@@ -1,43 +0,0 @@
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,
}
}

View File

@@ -4,8 +4,8 @@ package appmessage
// its respective RPC message
type GetBlockRequestMessage struct {
baseMessage
Hash string
IncludeTransactions bool
Hash string
IncludeTransactionVerboseData 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, includeTransactions bool) *GetBlockRequestMessage {
func NewGetBlockRequestMessage(hash string, includeTransactionVerboseData bool) *GetBlockRequestMessage {
return &GetBlockRequestMessage{
Hash: hash,
IncludeTransactions: includeTransactions,
Hash: hash,
IncludeTransactionVerboseData: includeTransactionVerboseData,
}
}
@@ -25,7 +25,7 @@ func NewGetBlockRequestMessage(hash string, includeTransactions bool) *GetBlockR
// its respective RPC message
type GetBlockResponseMessage struct {
baseMessage
Block *RPCBlock
BlockVerboseData *BlockVerboseData
Error *RPCError
}
@@ -39,3 +39,71 @@ 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
ChildrenHashes []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
Version uint16
}

View File

@@ -28,7 +28,6 @@ type GetBlockDAGInfoResponseMessage struct {
Difficulty float64
PastMedianTime int64
PruningPointHash string
VirtualDAAScore uint64
Error *RPCError
}

View File

@@ -23,7 +23,7 @@ func NewGetBlockTemplateRequestMessage(payAddress string) *GetBlockTemplateReque
// its respective RPC message
type GetBlockTemplateResponseMessage struct {
baseMessage
Block *RPCBlock
MsgBlock *MsgBlock
IsSynced bool
Error *RPCError
@@ -35,9 +35,9 @@ func (msg *GetBlockTemplateResponseMessage) Command() MessageCommand {
}
// NewGetBlockTemplateResponseMessage returns a instance of the message
func NewGetBlockTemplateResponseMessage(block *RPCBlock, isSynced bool) *GetBlockTemplateResponseMessage {
func NewGetBlockTemplateResponseMessage(msgBlock *MsgBlock, isSynced bool) *GetBlockTemplateResponseMessage {
return &GetBlockTemplateResponseMessage{
Block: block,
MsgBlock: msgBlock,
IsSynced: isSynced,
}
}

View File

@@ -4,9 +4,9 @@ package appmessage
// its respective RPC message
type GetBlocksRequestMessage struct {
baseMessage
LowHash string
IncludeBlocks bool
IncludeTransactions bool
LowHash string
IncludeBlockVerboseData bool
IncludeTransactionVerboseData 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, includeBlocks bool,
includeTransactions bool) *GetBlocksRequestMessage {
func NewGetBlocksRequestMessage(lowHash string, includeBlockVerboseData bool,
includeTransactionVerboseData bool) *GetBlocksRequestMessage {
return &GetBlocksRequestMessage{
LowHash: lowHash,
IncludeBlocks: includeBlocks,
IncludeTransactions: includeTransactions,
LowHash: lowHash,
IncludeBlockVerboseData: includeBlockVerboseData,
IncludeTransactionVerboseData: includeTransactionVerboseData,
}
}
@@ -28,8 +28,8 @@ func NewGetBlocksRequestMessage(lowHash string, includeBlocks bool,
// its respective RPC message
type GetBlocksResponseMessage struct {
baseMessage
BlockHashes []string
Blocks []*RPCBlock
BlockHashes []string
BlockVerboseData []*BlockVerboseData
Error *RPCError
}
@@ -40,6 +40,11 @@ func (msg *GetBlocksResponseMessage) Command() MessageCommand {
}
// NewGetBlocksResponseMessage returns a instance of the message
func NewGetBlocksResponseMessage() *GetBlocksResponseMessage {
return &GetBlocksResponseMessage{}
func NewGetBlocksResponseMessage(blockHashes []string, blockHexes []string,
blockVerboseData []*BlockVerboseData) *GetBlocksResponseMessage {
return &GetBlocksResponseMessage{
BlockHashes: blockHashes,
BlockVerboseData: blockVerboseData,
}
}

View File

@@ -11,8 +11,8 @@ func (msg *GetInfoRequestMessage) Command() MessageCommand {
return CmdGetInfoRequestMessage
}
// NewGetInfoRequestMessage returns a instance of the message
func NewGetInfoRequestMessage() *GetInfoRequestMessage {
// NewGeInfoRequestMessage returns a instance of the message
func NewGeInfoRequestMessage() *GetInfoRequestMessage {
return &GetInfoRequestMessage{}
}
@@ -20,8 +20,7 @@ func NewGetInfoRequestMessage() *GetInfoRequestMessage {
// its respective RPC message
type GetInfoResponseMessage struct {
baseMessage
P2PID string
MempoolSize uint64
P2PID string
Error *RPCError
}
@@ -32,9 +31,8 @@ func (msg *GetInfoResponseMessage) Command() MessageCommand {
}
// NewGetInfoResponseMessage returns a instance of the message
func NewGetInfoResponseMessage(p2pID string, mempoolSize uint64) *GetInfoResponseMessage {
func NewGetInfoResponseMessage(p2pID string) *GetInfoResponseMessage {
return &GetInfoResponseMessage{
P2PID: p2pID,
MempoolSize: mempoolSize,
P2PID: p2pID,
}
}

View File

@@ -28,8 +28,8 @@ type GetMempoolEntryResponseMessage struct {
// MempoolEntry represents a transaction in the mempool.
type MempoolEntry struct {
Fee uint64
Transaction *RPCTransaction
Fee uint64
TransactionVerboseData *TransactionVerboseData
}
// 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, transaction *RPCTransaction) *GetMempoolEntryResponseMessage {
func NewGetMempoolEntryResponseMessage(fee uint64, transactionVerboseData *TransactionVerboseData) *GetMempoolEntryResponseMessage {
return &GetMempoolEntryResponseMessage{
Entry: &MempoolEntry{
Fee: fee,
Transaction: transaction,
Fee: fee,
TransactionVerboseData: transactionVerboseData,
},
}
}

View File

@@ -37,7 +37,8 @@ func NewNotifyBlockAddedResponseMessage() *NotifyBlockAddedResponseMessage {
// its respective RPC message
type BlockAddedNotificationMessage struct {
baseMessage
Block *RPCBlock
Block *MsgBlock
BlockVerboseData *BlockVerboseData
}
// Command returns the protocol command string for the message
@@ -46,8 +47,9 @@ func (msg *BlockAddedNotificationMessage) Command() MessageCommand {
}
// NewBlockAddedNotificationMessage returns a instance of the message
func NewBlockAddedNotificationMessage(block *RPCBlock) *BlockAddedNotificationMessage {
func NewBlockAddedNotificationMessage(block *MsgBlock, blockVerboseData *BlockVerboseData) *BlockAddedNotificationMessage {
return &BlockAddedNotificationMessage{
Block: block,
Block: block,
BlockVerboseData: blockVerboseData,
}
}

View File

@@ -1,55 +0,0 @@
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,
}
}

View File

@@ -4,7 +4,7 @@ package appmessage
// its respective RPC message
type SubmitBlockRequestMessage struct {
baseMessage
Block *RPCBlock
Block *MsgBlock
}
// 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 *RPCBlock) *SubmitBlockRequestMessage {
func NewSubmitBlockRequestMessage(block *MsgBlock) *SubmitBlockRequestMessage {
return &SubmitBlockRequestMessage{
Block: block,
}
@@ -57,35 +57,3 @@ 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
}

View File

@@ -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,7 +59,6 @@ type RPCTransactionInput struct {
PreviousOutpoint *RPCOutpoint
SignatureScript string
Sequence uint64
VerboseData *RPCTransactionInputVerboseData
}
// RPCScriptPublicKey is a kaspad ScriptPublicKey representation
@@ -73,7 +72,6 @@ type RPCScriptPublicKey struct {
type RPCTransactionOutput struct {
Amount uint64
ScriptPublicKey *RPCScriptPublicKey
VerboseData *RPCTransactionOutputVerboseData
}
// RPCOutpoint is a kaspad outpoint representation meant to be used
@@ -88,25 +86,6 @@ type RPCOutpoint struct {
type RPCUTXOEntry struct {
Amount uint64
ScriptPublicKey *RPCScriptPublicKey
BlockDAAScore uint64
BlockBlueScore 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
}

View File

@@ -4,17 +4,23 @@ 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"
)
@@ -44,6 +50,8 @@ func (a *ComponentManager) Start() {
panics.Exit(log, fmt.Sprintf("Error starting the net adapter: %+v", err))
}
a.maybeSeedFromDNS()
a.connectionManager.Start()
}
@@ -64,8 +72,6 @@ func (a *ComponentManager) Stop() {
log.Errorf("Error stopping the net adapter: %+v", err)
}
a.protocolManager.Close()
return
}
@@ -74,13 +80,7 @@ func (a *ComponentManager) Stop() {
func NewComponentManager(cfg *config.Config, db infrastructuredatabase.Database, interrupt chan<- struct{}) (
*ComponentManager, error) {
consensusConfig := consensus.Config{
Params: *cfg.ActiveNetParams,
IsArchival: cfg.IsArchivalNode,
EnableSanityCheckPruningUTXOSet: cfg.EnableSanityCheckPruningUTXOSet,
}
domain, err := domain.New(&consensusConfig, db)
domain, err := domain.New(cfg.ActiveNetParams, db, cfg.IsArchivalNode)
if err != nil {
return nil, err
}
@@ -153,6 +153,23 @@ func setupRPC(
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()

View File

@@ -1,57 +0,0 @@
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
}

View File

@@ -37,14 +37,12 @@ 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)
acceptedTransactions, err := f.Domain().MiningManager().HandleNewBlockTransactions(newBlock.Transactions)
_, 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)
@@ -56,7 +54,7 @@ func (f *FlowContext) OnNewBlock(block *externalapi.DomainBlock,
}
}
return f.broadcastTransactionsAfterBlockAdded(newBlocks, allAcceptedTransactions)
return nil
}
// OnPruningPointUTXOSetOverride calls the handler function whenever the UTXO set
@@ -69,9 +67,9 @@ func (f *FlowContext) OnPruningPointUTXOSetOverride() error {
}
func (f *FlowContext) broadcastTransactionsAfterBlockAdded(
addedBlocks []*externalapi.DomainBlock, transactionsAcceptedToMempool []*externalapi.DomainTransaction) error {
block *externalapi.DomainBlock, transactionsAcceptedToMempool []*externalapi.DomainTransaction) error {
f.updateTransactionsToRebroadcast(addedBlocks)
f.updateTransactionsToRebroadcast(block)
// Don't relay transactions when in IBD.
if f.IsIBDRunning() {

View File

@@ -61,8 +61,6 @@ type FlowContext struct {
orphans map[externalapi.DomainHash]*externalapi.DomainBlock
orphansMutex sync.RWMutex
shutdownChan chan struct{}
}
// New returns a new instance of FlowContext.
@@ -81,21 +79,9 @@ 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

View File

@@ -25,17 +25,14 @@ func (f *FlowContext) AddTransaction(tx *externalapi.DomainTransaction) error {
return f.Broadcast(inv)
}
func (f *FlowContext) updateTransactionsToRebroadcast(addedBlocks []*externalapi.DomainBlock) {
func (f *FlowContext) updateTransactionsToRebroadcast(block *externalapi.DomainBlock) {
f.transactionsToRebroadcastLock.Lock()
defer f.transactionsToRebroadcastLock.Unlock()
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))
}
// 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))
}
}

View File

@@ -50,7 +50,7 @@ func (flow *handleRequestHeadersFlow) 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
}

View File

@@ -13,7 +13,6 @@ import (
// SendPingsContext is the interface for the context needed for the SendPings flow.
type SendPingsContext interface {
ShutdownChan() <-chan struct{}
}
type sendPingsFlow struct {
@@ -40,13 +39,7 @@ func (flow *sendPingsFlow) start() error {
ticker := time.NewTicker(pingInterval)
defer ticker.Stop()
for {
select {
case <-flow.ShutdownChan():
return nil
case <-ticker.C:
}
for range ticker.C {
nonce, err := random.Uint64()
if err != nil {
return err
@@ -69,4 +62,5 @@ func (flow *sendPingsFlow) start() error {
}
flow.peer.SetPingIdle()
}
return nil
}

View File

@@ -10,7 +10,6 @@ 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"
@@ -130,15 +129,7 @@ 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) {
func (f *fakeRelayInvsContext) GetBlockChildren(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
panic(errors.Errorf("called unimplemented function from test '%s'", f.testName))
}
@@ -190,7 +181,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) (hashes []*externalapi.DomainHash, actualHighHash *externalapi.DomainHash, err error) {
func (f *fakeRelayInvsContext) GetHashesBetween(lowHash, highHash *externalapi.DomainHash, maxBlueScoreDifference uint64) ([]*externalapi.DomainHash, error) {
panic(errors.Errorf("called unimplemented function from test '%s'", f.testName))
}
@@ -1505,7 +1496,7 @@ func TestHandleRelayInvs(t *testing.T) {
},
}
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
for _, test := range tests {
// This is done to avoid race condition
@@ -1520,7 +1511,7 @@ func TestHandleRelayInvs(t *testing.T) {
errChan := make(chan error)
context := &fakeRelayInvsContext{
testName: test.name,
params: &consensusConfig.Params,
params: params,
finishedIBD: make(chan struct{}),
trySetIBDRunningResponse: true,

View File

@@ -1,16 +1,15 @@
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{}
@@ -20,7 +19,7 @@ func (f fakeReceiveAddressesContext) AddressManager() *addressmanager.AddressMan
}
func TestReceiveAddressesErrors(t *testing.T) {
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
incomingRoute := router.NewRoute()
outgoingRoute := router.NewRoute()
peer := peerpkg.New(nil)

View File

@@ -1,189 +0,0 @@
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)
}
})
}

View File

@@ -1,88 +0,0 @@
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)
}
})
}

View File

@@ -2,9 +2,6 @@ package protocol
import (
"fmt"
"github.com/pkg/errors"
"sync"
"sync/atomic"
"github.com/kaspanet/kaspad/domain"
@@ -20,9 +17,7 @@ import (
// Manager manages the p2p protocol
type Manager struct {
context *flowcontext.FlowContext
routersWaitGroup sync.WaitGroup
isClosed uint32
context *flowcontext.FlowContext
}
// NewManager creates a new instance of the p2p protocol manager
@@ -37,18 +32,6 @@ 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()
@@ -70,13 +53,11 @@ 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, flowsWaitGroup *sync.WaitGroup) error {
flowsWaitGroup.Add(len(flows))
func (m *Manager) runFlows(flows []*flow, peer *peerpkg.Peer, errChan <-chan error) error {
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()
})
}

View File

@@ -1,20 +1,20 @@
package protocol
import (
"sync"
"sync/atomic"
"github.com/kaspanet/kaspad/app/protocol/flows/rejects"
"github.com/kaspanet/kaspad/infrastructure/network/connmanager"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/protocol/flows/addressexchange"
"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"
@@ -41,13 +41,6 @@ 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)
@@ -86,17 +79,11 @@ func (m *Manager) routerInitializer(router *routerpkg.Router, netConnection *net
removeHandshakeRoutes(router)
flowsWaitGroup := &sync.WaitGroup{}
err = m.runFlows(flows, peer, errChan, flowsWaitGroup)
err = m.runFlows(flows, peer, errChan)
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()
})
}
@@ -106,7 +93,7 @@ func (m *Manager) handleError(err error, netConnection *netadapter.NetConnection
log.Warnf("Banning %s (reason: %s)", netConnection, protocolErr.Cause)
err := m.context.ConnectionManager().Ban(netConnection)
if err != nil && !errors.Is(err, connmanager.ErrCannotBanPermanent) {
if !errors.Is(err, connmanager.ErrCannotBanPermanent) {
panic(err)
}
@@ -251,7 +238,7 @@ func (m *Manager) registerTransactionRelayFlow(router *routerpkg.Router, isStopp
outgoingRoute := router.OutgoingRoute()
return []*flow{
m.registerFlowWithCapacity("HandleRelayedTransactions", 10_000, router,
m.registerFlow("HandleRelayedTransactions", 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)
@@ -287,24 +274,6 @@ 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) {

View File

@@ -64,22 +64,17 @@ 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
}
rpcBlock := appmessage.DomainBlockToRPCBlock(block)
err = m.context.PopulateBlockWithVerboseData(rpcBlock, block.Header, block, false)
msgBlock := appmessage.DomainBlockToMsgBlock(block)
blockVerboseData, err := m.context.BuildBlockVerboseData(block.Header, block, false)
if err != nil {
return err
}
blockAddedNotification := appmessage.NewBlockAddedNotificationMessage(rpcBlock)
blockAddedNotification := appmessage.NewBlockAddedNotificationMessage(msgBlock, blockVerboseData)
return m.context.NotificationManager.NotifyBlockAdded(blockAddedNotification)
}
@@ -158,19 +153,6 @@ 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()

View File

@@ -44,8 +44,6 @@ var handlers = map[appmessage.MessageCommand]handler{
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) {

View File

@@ -30,7 +30,6 @@ type NotificationListener struct {
propagateFinalityConflictResolvedNotifications bool
propagateUTXOsChangedNotifications bool
propagateVirtualSelectedParentBlueScoreChangedNotifications bool
propagateVirtualDaaScoreChangedNotifications bool
propagatePruningPointUTXOSetOverrideNotifications bool
propagateUTXOsChangedNotificationAddresses map[utxoindex.ScriptPublicKeyString]*UTXOsChangedNotificationAddress
@@ -182,25 +181,6 @@ 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 {
@@ -328,12 +308,6 @@ func (nl *NotificationListener) PropagateVirtualSelectedParentBlueScoreChangedNo
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() {

View File

@@ -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},
BlockDAAScore: utxoEntry.BlockDAAScore(),
BlockBlueScore: utxoEntry.BlockBlueScore(),
IsCoinbase: utxoEntry.IsCoinbase(),
},
})

View File

@@ -2,10 +2,14 @@ package rpccontext
import (
"encoding/hex"
"fmt"
"math"
"math/big"
"strconv"
difficultyPackage "github.com/kaspanet/kaspad/util/difficulty"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/util/difficulty"
"github.com/pkg/errors"
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
@@ -13,6 +17,8 @@ import (
"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"
@@ -22,6 +28,79 @@ 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")
}
childrenHashes, err := ctx.Domain.Consensus().GetBlockChildren(hash)
if err != nil {
return nil, err
}
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()),
ChildrenHashes: hashes.ToStrings(childrenHashes),
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 {
@@ -29,7 +108,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 := difficultyPackage.CompactToBig(bits)
target := difficulty.CompactToBig(bits)
difficulty := new(big.Rat).SetFrac(params.PowMax, target)
diff, _ := difficulty.Float64()
@@ -40,128 +119,106 @@ func (ctx *Context) GetDifficultyRatio(bits uint32, params *dagconfig.Params) fl
return diff
}
// 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 {
// 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) {
blockHash := consensushashing.HeaderHash(domainBlockHeader)
onEnd := logger.LogAndMeasureExecutionTime(log, "BuildTransactionVerboseData")
defer onEnd()
blockInfo, err := ctx.Domain.Consensus().GetBlockInfo(blockHash)
if err != nil {
return err
var payloadHash string
if tx.SubnetworkID != subnetworks.SubnetworkIDNative {
payloadHash = tx.PayloadHash.String()
}
if blockInfo.BlockStatus == externalapi.StatusInvalid {
return errors.Wrap(ErrBuildBlockVerboseDataInvalidBlock, "cannot build verbose data for "+
"invalid block")
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),
}
_, selectedParentHash, childrenHashes, err := ctx.Domain.Consensus().GetBlockRelations(blockHash)
if err != nil {
return err
if blockHeader != nil {
txReply.Time = uint64(blockHeader.TimeInMilliseconds())
txReply.BlockTime = uint64(blockHeader.TimeInMilliseconds())
txReply.BlockHash = blockHash
}
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()
}
return txReply, nil
}
if blockInfo.BlockStatus == externalapi.StatusHeaderOnly {
return nil
}
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)
// 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
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),
}
inputs[i] = input
}
transactionIDs := make([]string, len(domainBlock.Transactions))
for i, transaction := range domainBlock.Transactions {
transactionIDs[i] = consensushashing.TransactionID(transaction).String()
}
block.VerboseData.TransactionIDs = transactionIDs
return inputs
}
if includeTransactionVerboseData {
for _, transaction := range block.Transactions {
err := ctx.PopulateTransactionWithVerboseData(transaction, domainBlockHeader)
if err != nil {
return err
// 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
}
}
}
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
if !passesFilter {
continue
}
output := &appmessage.TransactionVerboseOutput{}
output.Index = uint32(i)
output.Value = transactionOutput.Value
output.ScriptPubKey = &appmessage.ScriptPubKeyResult{
Version: transactionOutput.ScriptPublicKey.Version,
Address: encodedAddr,
Hex: hex.EncodeToString(transactionOutput.ScriptPublicKey.Script),
Type: scriptClass.String(),
}
outputs[i] = output
}
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
return outputs
}

View File

@@ -1,39 +0,0 @@
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
}

View File

@@ -20,7 +20,7 @@ func HandleGetBlock(context *rpccontext.Context, _ *router.Router, request appme
return errorMessage, nil
}
block, err := context.Domain.Consensus().GetBlockEvenIfHeaderOnly(hash)
header, err := context.Domain.Consensus().GetBlockHeader(hash)
if err != nil {
errorMessage := &appmessage.GetBlockResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Block %s not found", hash)
@@ -29,13 +29,7 @@ func HandleGetBlock(context *rpccontext.Context, _ *router.Router, request appme
response := appmessage.NewGetBlockResponseMessage()
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)
blockVerboseData, err := context.BuildBlockVerboseData(header, nil, getBlockRequest.IncludeTransactionVerboseData)
if err != nil {
if errors.Is(err, rpccontext.ErrBuildBlockVerboseDataInvalidBlock) {
errorMessage := &appmessage.GetBlockResponseMessage{}
@@ -45,5 +39,7 @@ func HandleGetBlock(context *rpccontext.Context, _ *router.Router, request appme
return nil, err
}
response.BlockVerboseData = blockVerboseData
return response, nil
}

View File

@@ -35,7 +35,6 @@ 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 {

View File

@@ -31,12 +31,12 @@ func HandleGetBlockTemplate(context *rpccontext.Context, _ *router.Router, reque
if err != nil {
return nil, err
}
rpcBlock := appmessage.DomainBlockToRPCBlock(templateBlock)
msgBlock := appmessage.DomainBlockToMsgBlock(templateBlock)
isSynced, err := context.ProtocolManager.ShouldMine()
if err != nil {
return nil, err
}
return appmessage.NewGetBlockTemplateResponseMessage(rpcBlock, isSynced), nil
return appmessage.NewGetBlockTemplateResponseMessage(msgBlock, isSynced), nil
}

View File

@@ -18,11 +18,11 @@ const (
func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
getBlocksRequest := request.(*appmessage.GetBlocksRequestMessage)
// Validate that user didn't set IncludeTransactions without setting IncludeBlocks
if !getBlocksRequest.IncludeBlocks && getBlocksRequest.IncludeTransactions {
// Validate that user didn't set IncludeTransactionVerboseData without setting IncludeBlockVerboseData
if !getBlocksRequest.IncludeBlockVerboseData && getBlocksRequest.IncludeTransactionVerboseData {
return &appmessage.GetBlocksResponseMessage{
Error: appmessage.RPCErrorf(
"If includeTransactions is set, then includeBlockVerboseData must be set as well"),
"If includeTransactionVerboseData is set, then includeBlockVerboseData must be set as well"),
}, nil
}
@@ -55,7 +55,8 @@ func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appm
if err != nil {
return nil, err
}
blockHashes, highHash, err := context.Domain.Consensus().GetHashesBetween(lowHash, virtualSelectedParent, maxBlocksInGetBlocksResponse)
blockHashes, err := context.Domain.Consensus().GetHashesBetween(
lowHash, virtualSelectedParent, maxBlocksInGetBlocksResponse)
if err != nil {
return nil, err
}
@@ -63,10 +64,9 @@ func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appm
// prepend low hash to make it inclusive
blockHashes = append([]*externalapi.DomainHash{lowHash}, blockHashes...)
// 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) {
// If there are no maxBlocksInGetBlocksResponse between lowHash and virtualSelectedParent -
// add virtualSelectedParent's anticone
if len(blockHashes) < maxBlocksInGetBlocksResponse {
virtualSelectedParentAnticone, err := context.Domain.Consensus().Anticone(virtualSelectedParent)
if err != nil {
return nil, err
@@ -81,28 +81,26 @@ func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appm
}
// Prepare the response
response := appmessage.NewGetBlocksResponseMessage()
response.BlockHashes = hashes.ToStrings(blockHashes)
if getBlocksRequest.IncludeBlocks {
rpcBlocks := make([]*appmessage.RPCBlock, len(blockHashes))
for i, blockHash := range blockHashes {
block, err := context.Domain.Consensus().GetBlockEvenIfHeaderOnly(blockHash)
if err != nil {
return nil, err
}
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
response := &appmessage.GetBlocksResponseMessage{
BlockHashes: hashes.ToStrings(blockHashes),
}
// Retrieve all block data in case BlockVerboseData was requested
if getBlocksRequest.IncludeBlockVerboseData {
response.BlockVerboseData = make([]*appmessage.BlockVerboseData, len(blockHashes))
for i, blockHash := range blockHashes {
blockHeader, err := context.Domain.Consensus().GetBlockHeader(blockHash)
if err != nil {
return nil, err
}
blockVerboseData, err := context.BuildBlockVerboseData(blockHeader, nil,
getBlocksRequest.IncludeTransactionVerboseData)
if err != nil {
return nil, err
}
response.BlockVerboseData[i] = blockVerboseData
}
}
return response, nil
}

View File

@@ -5,8 +5,6 @@ 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"
@@ -15,6 +13,7 @@ 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"
)
@@ -27,18 +26,16 @@ 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, consensusConfig *consensus.Config) {
stagingArea := model.NewStagingArea()
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
factory := consensus.NewFactory()
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestHandleGetBlocks")
tc, teardown, err := factory.NewTestConsensus(params, false, "TestHandleGetBlocks")
if err != nil {
t.Fatalf("Error setting up consensus: %+v", err)
}
defer teardown(false)
fakeContext := rpccontext.Context{
Config: &config.Config{Flags: &config.Flags{NetworkFlags: config.NetworkFlags{ActiveNetParams: &consensusConfig.Params}}},
Config: &config.Config{Flags: &config.Flags{NetworkFlags: config.NetworkFlags{ActiveNetParams: params}}},
Domain: fakeDomain{tc},
}
@@ -58,7 +55,7 @@ func TestHandleGetBlocks(t *testing.T) {
antipast := make([]*externalapi.DomainHash, 0, len(slice))
for _, blockHash := range slice {
isInPastOfPovBlock, err := tc.DAGTopologyManager().IsAncestorOf(stagingArea, blockHash, povBlock)
isInPastOfPovBlock, err := tc.DAGTopologyManager().IsAncestorOf(blockHash, povBlock)
if err != nil {
t.Fatalf("Failed doing reachability check: '%v'", err)
}
@@ -80,7 +77,7 @@ func TestHandleGetBlocks(t *testing.T) {
// \ | /
// etc.
expectedOrder := make([]*externalapi.DomainHash, 0, 40)
mergingBlock := consensusConfig.GenesisHash
mergingBlock := params.GenesisHash
for i := 0; i < 10; i++ {
splitBlocks := make([]*externalapi.DomainHash, 0, 3)
for j := 0; j < 3; j++ {
@@ -90,7 +87,7 @@ func TestHandleGetBlocks(t *testing.T) {
}
splitBlocks = append(splitBlocks, blockHash)
}
sort.Sort(sort.Reverse(testutils.NewTestGhostDAGSorter(stagingArea, splitBlocks, tc, t)))
sort.Sort(sort.Reverse(testutils.NewTestGhostDAGSorter(splitBlocks, tc, t)))
restOfSplitBlocks, selectedParent := splitBlocks[:len(splitBlocks)-1], splitBlocks[len(splitBlocks)-1]
expectedOrder = append(expectedOrder, selectedParent)
expectedOrder = append(expectedOrder, restOfSplitBlocks...)
@@ -133,13 +130,13 @@ func TestHandleGetBlocks(t *testing.T) {
virtualSelectedParent, actualBlocks.BlockHashes)
}
expectedOrder = append([]*externalapi.DomainHash{consensusConfig.GenesisHash}, expectedOrder...)
expectedOrder = append([]*externalapi.DomainHash{params.GenesisHash}, expectedOrder...)
actualOrder := getBlocks(nil)
if !reflect.DeepEqual(actualOrder.BlockHashes, hashes.ToStrings(expectedOrder)) {
t.Fatalf("TestHandleGetBlocks \nexpected: %v \nactual:\n%v", expectedOrder, actualOrder.BlockHashes)
}
requestAllExplictly := getBlocks(consensusConfig.GenesisHash)
requestAllExplictly := getBlocks(params.GenesisHash)
if !reflect.DeepEqual(requestAllExplictly.BlockHashes, hashes.ToStrings(expectedOrder)) {
t.Fatalf("TestHandleGetBlocks \nexpected: \n%v\n. actual:\n%v", expectedOrder, requestAllExplictly.BlockHashes)
}

View File

@@ -8,10 +8,6 @@ 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(),
uint64(context.Domain.MiningManager().TransactionCount()),
)
response := appmessage.NewGetInfoResponseMessage(context.NetAdapter.ID().String())
return response, nil
}

View File

@@ -3,22 +3,25 @@ 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 _, transaction := range transactions {
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
for _, tx := range transactions {
transactionVerboseData, err := context.BuildTransactionVerboseData(
tx, consensushashing.TransactionID(tx).String(), nil, "")
if err != nil {
return nil, err
}
entries = append(entries, &appmessage.MempoolEntry{
Fee: transaction.Fee,
Transaction: rpcTransaction,
Fee: tx.Fee,
TransactionVerboseData: transactionVerboseData,
})
}

View File

@@ -24,11 +24,12 @@ func HandleGetMempoolEntry(context *rpccontext.Context, _ *router.Router, reques
errorMessage.Error = appmessage.RPCErrorf("Transaction %s was not found", transactionID)
return errorMessage, nil
}
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
err = context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
transactionVerboseData, err := context.BuildTransactionVerboseData(
transaction, getMempoolEntryRequest.TxID, nil, "")
if err != nil {
return nil, err
}
return appmessage.NewGetMempoolEntryResponseMessage(transaction.Fee, rpcTransaction), nil
return appmessage.NewGetMempoolEntryResponseMessage(transaction.Fee, transactionVerboseData), nil
}

View File

@@ -1,19 +0,0 @@
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
}

View File

@@ -14,6 +14,9 @@ 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"),
@@ -21,15 +24,7 @@ func HandleSubmitBlock(context *rpccontext.Context, _ *router.Router, request ap
}, nil
}
domainBlock, err := appmessage.RPCBlockToDomainBlock(submitBlockRequest.Block)
if err != nil {
return &appmessage.SubmitBlockResponseMessage{
Error: appmessage.RPCErrorf("Could not parse block: %s", err),
RejectReason: appmessage.RejectReasonBlockInvalid,
}, nil
}
err = context.ProtocolManager.AddBlock(domainBlock)
err := context.ProtocolManager.AddBlock(domainBlock)
if err != nil {
isProtocolOrRuleError := errors.As(err, &ruleerrors.RuleError{}) || errors.As(err, &protocolerrors.ProtocolError{})
if !isProtocolOrRuleError {

View File

@@ -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))
err := context.AddressManager.Unban(appmessage.NewNetAddressIPPort(ip, 0, 0))
if err != nil {
errorMessage := &appmessage.UnbanResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Could not unban IP: %s", err)

View File

@@ -1,95 +1,11 @@
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)
[*] 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)

View File

@@ -1,9 +0,0 @@
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)

View File

@@ -1,26 +0,0 @@
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
}

View File

@@ -1,27 +0,0 @@
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)
}

View File

@@ -48,10 +48,6 @@ 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() {

View File

@@ -24,7 +24,6 @@ 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{}),

View File

@@ -27,8 +27,7 @@ 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." +
"\nFor optional parameters- use '-' without quotes to not pass the parameter.\n"
"\n\nUse `kaspactl --list-commands` to get a list of all commands and their parameters"
remainingArgs, err := parser.Parse()
if err != nil {
return nil, err

View File

@@ -5,32 +5,72 @@ import (
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/infrastructure/network/rpcclient"
"github.com/pkg/errors"
"sync"
"sync/atomic"
"time"
)
const minerTimeout = 10 * time.Second
type minerClient struct {
*rpcclient.RPCClient
isReconnecting uint32
clientLock sync.RWMutex
rpcClient *rpcclient.RPCClient
cfg *configFlags
blockAddedNotificationChan chan struct{}
}
func (mc *minerClient) safeRPCClient() *rpcclient.RPCClient {
mc.clientLock.RLock()
defer mc.clientLock.RUnlock()
return mc.rpcClient
}
func (mc *minerClient) reconnect() {
swapped := atomic.CompareAndSwapUint32(&mc.isReconnecting, 0, 1)
if !swapped {
return
}
defer atomic.StoreUint32(&mc.isReconnecting, 0)
mc.clientLock.Lock()
defer mc.clientLock.Unlock()
retryDuration := time.Second
const maxRetryDuration = time.Minute
log.Infof("Reconnecting RPC connection")
for {
err := mc.connect()
if err == nil {
return
}
if retryDuration < time.Minute {
retryDuration *= 2
} else {
retryDuration = maxRetryDuration
}
log.Errorf("Got error '%s' while reconnecting. Trying again in %s", err, retryDuration)
time.Sleep(retryDuration)
}
}
func (mc *minerClient) connect() error {
rpcAddress, err := mc.cfg.NetParams().NormalizeRPCServerAddress(mc.cfg.RPCServer)
if err != nil {
return err
}
rpcClient, err := rpcclient.NewRPCClient(rpcAddress)
mc.rpcClient, err = rpcclient.NewRPCClient(rpcAddress)
if err != nil {
return err
}
mc.RPCClient = rpcClient
mc.SetTimeout(minerTimeout)
mc.SetLogger(backendLog, logger.LevelTrace)
mc.rpcClient.SetTimeout(minerTimeout)
mc.rpcClient.SetLogger(backendLog, logger.LevelTrace)
err = mc.RegisterForBlockAddedNotifications(func(_ *appmessage.BlockAddedNotificationMessage) {
err = mc.rpcClient.RegisterForBlockAddedNotifications(func(_ *appmessage.BlockAddedNotificationMessage) {
select {
case mc.blockAddedNotificationChan <- struct{}{}:
default:

View File

@@ -24,9 +24,9 @@ const (
var (
// Default configuration options
defaultAppDir = util.AppDir("kaspaminer", false)
defaultLogFile = filepath.Join(defaultAppDir, defaultLogFilename)
defaultErrLogFile = filepath.Join(defaultAppDir, defaultErrLogFilename)
defaultHomeDir = util.AppDataDir("kaspaminer", false)
defaultLogFile = filepath.Join(defaultHomeDir, defaultLogFilename)
defaultErrLogFile = filepath.Join(defaultHomeDir, defaultErrLogFilename)
defaultRPCServer = "localhost"
)

View File

@@ -40,7 +40,7 @@ func main() {
if err != nil {
panic(errors.Wrap(err, "error connecting to the RPC server"))
}
defer client.Disconnect()
defer client.safeRPCClient().Disconnect()
miningAddr, err := util.DecodeAddress(cfg.MiningAddr, cfg.ActiveNetParams.Prefix)
if err != nil {

View File

@@ -6,14 +6,18 @@ import (
"sync/atomic"
"time"
"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/domain/consensus/model/pow"
"github.com/kaspanet/kaspad/util/difficulty"
"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/infrastructure/network/netadapter/router"
"github.com/kaspanet/kaspad/util"
"github.com/pkg/errors"
)
@@ -40,32 +44,34 @@ 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 {
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()
expectedDurationForWindow = time.Duration(float64(windowSize)/targetBlocksPerSecond) * time.Second
windowExpectedEndTime = time.Now().Add(expectedDurationForWindow)
}
windowStart := time.Now()
for blockIndex := 1; ; blockIndex++ {
blockInWindowIndex := 0
sleepTime := 0 * time.Second
for {
foundBlockChan <- mineNextBlock(mineWhenNotSynced)
if hasBlockRateTarget {
<-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()
blockInWindowIndex++
if blockInWindowIndex == windowSize-1 {
deviation := windowExpectedEndTime.Sub(time.Now())
if deviation > 0 {
sleepTime = deviation / windowSize
log.Infof("Finished to mine %d blocks %s earlier than expected. Setting the miner "+
"to sleep %s between blocks to compensate",
windowSize, deviation, sleepTime)
}
blockInWindowIndex = 0
windowExpectedEndTime = time.Now().Add(expectedDurationForWindow)
}
time.Sleep(sleepTime)
}
}
})
@@ -110,17 +116,13 @@ func logHashRate() {
func handleFoundBlock(client *minerClient, block *externalapi.DomainBlock) error {
blockHash := consensushashing.BlockHash(block)
log.Infof("Submitting block %s to %s", blockHash, client.Address())
log.Infof("Submitting block %s to %s", blockHash, client.safeRPCClient().Address())
rejectReason, err := client.SubmitBlock(block)
rejectReason, err := client.safeRPCClient().SubmitBlock(block)
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())
log.Warnf("Got timeout while submitting block %s to %s: %s", blockHash, client.safeRPCClient().Address(), err)
client.reconnect()
return nil
}
if rejectReason == appmessage.RejectReasonIsInIBD {
@@ -129,7 +131,7 @@ func handleFoundBlock(client *minerClient, block *externalapi.DomainBlock) error
time.Sleep(waitTime)
return nil
}
return errors.Wrapf(err, "Error submitting block %s to %s", blockHash, client.Address())
return errors.Wrapf(err, "Error submitting block %s to %s", blockHash, client.safeRPCClient().Address())
}
return nil
}
@@ -188,29 +190,17 @@ func getBlockForMining(mineWhenNotSynced bool) *externalapi.DomainBlock {
func templatesLoop(client *minerClient, miningAddr util.Address, errChan chan error) {
getBlockTemplate := func() {
template, err := client.GetBlockTemplate(miningAddr.String())
template, err := client.safeRPCClient().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())
log.Warnf("Got timeout while requesting block template from %s: %s", client.safeRPCClient().Address(), err)
client.reconnect()
return
}
if err != nil {
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())
errChan <- errors.Wrapf(err, "Error getting block template from %s", client.safeRPCClient().Address())
return
}
templatemanager.Set(template)
}
getBlockTemplate()

View File

@@ -23,14 +23,10 @@ func Get() (*externalapi.DomainBlock, bool) {
}
// Set sets the current template to work on
func Set(template *appmessage.GetBlockTemplateResponseMessage) error {
block, err := appmessage.RPCBlockToDomainBlock(template.Block)
if err != nil {
return err
}
func Set(template *appmessage.GetBlockTemplateResponseMessage) {
block := appmessage.MsgBlockToDomainBlock(template.MsgBlock)
lock.Lock()
defer lock.Unlock()
currentTemplate = block
isSynced = template.IsSynced
return nil
}

View File

@@ -1,31 +0,0 @@
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
}

View File

@@ -1,35 +0,0 @@
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
}

View File

@@ -1,14 +0,0 @@
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)
}

View File

@@ -1,229 +0,0 @@
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
}
}

View File

@@ -1,78 +0,0 @@
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
}

View File

@@ -1,34 +0,0 @@
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
}

View File

@@ -1,18 +0,0 @@
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
}

View File

@@ -1,3 +0,0 @@
//go:generate protoc --go_out=. --go-grpc_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative kaspawalletd.proto
package pb

View File

@@ -1,730 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.12.3
// source: kaspawalletd.proto
package pb
import (
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type GetBalanceRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *GetBalanceRequest) Reset() {
*x = GetBalanceRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_kaspawalletd_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetBalanceRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetBalanceRequest) ProtoMessage() {}
func (x *GetBalanceRequest) ProtoReflect() protoreflect.Message {
mi := &file_kaspawalletd_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetBalanceRequest.ProtoReflect.Descriptor instead.
func (*GetBalanceRequest) Descriptor() ([]byte, []int) {
return file_kaspawalletd_proto_rawDescGZIP(), []int{0}
}
type GetBalanceResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Available uint64 `protobuf:"varint,1,opt,name=available,proto3" json:"available,omitempty"`
Pending uint64 `protobuf:"varint,2,opt,name=pending,proto3" json:"pending,omitempty"`
}
func (x *GetBalanceResponse) Reset() {
*x = GetBalanceResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_kaspawalletd_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetBalanceResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetBalanceResponse) ProtoMessage() {}
func (x *GetBalanceResponse) ProtoReflect() protoreflect.Message {
mi := &file_kaspawalletd_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetBalanceResponse.ProtoReflect.Descriptor instead.
func (*GetBalanceResponse) Descriptor() ([]byte, []int) {
return file_kaspawalletd_proto_rawDescGZIP(), []int{1}
}
func (x *GetBalanceResponse) GetAvailable() uint64 {
if x != nil {
return x.Available
}
return 0
}
func (x *GetBalanceResponse) GetPending() uint64 {
if x != nil {
return x.Pending
}
return 0
}
type CreateUnsignedTransactionRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
Amount uint64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"`
}
func (x *CreateUnsignedTransactionRequest) Reset() {
*x = CreateUnsignedTransactionRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_kaspawalletd_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CreateUnsignedTransactionRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreateUnsignedTransactionRequest) ProtoMessage() {}
func (x *CreateUnsignedTransactionRequest) ProtoReflect() protoreflect.Message {
mi := &file_kaspawalletd_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CreateUnsignedTransactionRequest.ProtoReflect.Descriptor instead.
func (*CreateUnsignedTransactionRequest) Descriptor() ([]byte, []int) {
return file_kaspawalletd_proto_rawDescGZIP(), []int{2}
}
func (x *CreateUnsignedTransactionRequest) GetAddress() string {
if x != nil {
return x.Address
}
return ""
}
func (x *CreateUnsignedTransactionRequest) GetAmount() uint64 {
if x != nil {
return x.Amount
}
return 0
}
type CreateUnsignedTransactionResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
UnsignedTransaction []byte `protobuf:"bytes,1,opt,name=unsignedTransaction,proto3" json:"unsignedTransaction,omitempty"`
}
func (x *CreateUnsignedTransactionResponse) Reset() {
*x = CreateUnsignedTransactionResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_kaspawalletd_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CreateUnsignedTransactionResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreateUnsignedTransactionResponse) ProtoMessage() {}
func (x *CreateUnsignedTransactionResponse) ProtoReflect() protoreflect.Message {
mi := &file_kaspawalletd_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CreateUnsignedTransactionResponse.ProtoReflect.Descriptor instead.
func (*CreateUnsignedTransactionResponse) Descriptor() ([]byte, []int) {
return file_kaspawalletd_proto_rawDescGZIP(), []int{3}
}
func (x *CreateUnsignedTransactionResponse) GetUnsignedTransaction() []byte {
if x != nil {
return x.UnsignedTransaction
}
return nil
}
type GetReceiveAddressRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *GetReceiveAddressRequest) Reset() {
*x = GetReceiveAddressRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_kaspawalletd_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetReceiveAddressRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetReceiveAddressRequest) ProtoMessage() {}
func (x *GetReceiveAddressRequest) ProtoReflect() protoreflect.Message {
mi := &file_kaspawalletd_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetReceiveAddressRequest.ProtoReflect.Descriptor instead.
func (*GetReceiveAddressRequest) Descriptor() ([]byte, []int) {
return file_kaspawalletd_proto_rawDescGZIP(), []int{4}
}
type GetReceiveAddressResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
}
func (x *GetReceiveAddressResponse) Reset() {
*x = GetReceiveAddressResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_kaspawalletd_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetReceiveAddressResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetReceiveAddressResponse) ProtoMessage() {}
func (x *GetReceiveAddressResponse) ProtoReflect() protoreflect.Message {
mi := &file_kaspawalletd_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetReceiveAddressResponse.ProtoReflect.Descriptor instead.
func (*GetReceiveAddressResponse) Descriptor() ([]byte, []int) {
return file_kaspawalletd_proto_rawDescGZIP(), []int{5}
}
func (x *GetReceiveAddressResponse) GetAddress() string {
if x != nil {
return x.Address
}
return ""
}
type BroadcastRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Transaction []byte `protobuf:"bytes,1,opt,name=transaction,proto3" json:"transaction,omitempty"`
}
func (x *BroadcastRequest) Reset() {
*x = BroadcastRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_kaspawalletd_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *BroadcastRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BroadcastRequest) ProtoMessage() {}
func (x *BroadcastRequest) ProtoReflect() protoreflect.Message {
mi := &file_kaspawalletd_proto_msgTypes[6]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BroadcastRequest.ProtoReflect.Descriptor instead.
func (*BroadcastRequest) Descriptor() ([]byte, []int) {
return file_kaspawalletd_proto_rawDescGZIP(), []int{6}
}
func (x *BroadcastRequest) GetTransaction() []byte {
if x != nil {
return x.Transaction
}
return nil
}
type BroadcastResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
TxID string `protobuf:"bytes,1,opt,name=txID,proto3" json:"txID,omitempty"`
}
func (x *BroadcastResponse) Reset() {
*x = BroadcastResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_kaspawalletd_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *BroadcastResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BroadcastResponse) ProtoMessage() {}
func (x *BroadcastResponse) ProtoReflect() protoreflect.Message {
mi := &file_kaspawalletd_proto_msgTypes[7]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BroadcastResponse.ProtoReflect.Descriptor instead.
func (*BroadcastResponse) Descriptor() ([]byte, []int) {
return file_kaspawalletd_proto_rawDescGZIP(), []int{7}
}
func (x *BroadcastResponse) GetTxID() string {
if x != nil {
return x.TxID
}
return ""
}
type ShutdownRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *ShutdownRequest) Reset() {
*x = ShutdownRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_kaspawalletd_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ShutdownRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ShutdownRequest) ProtoMessage() {}
func (x *ShutdownRequest) ProtoReflect() protoreflect.Message {
mi := &file_kaspawalletd_proto_msgTypes[8]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ShutdownRequest.ProtoReflect.Descriptor instead.
func (*ShutdownRequest) Descriptor() ([]byte, []int) {
return file_kaspawalletd_proto_rawDescGZIP(), []int{8}
}
type ShutdownResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *ShutdownResponse) Reset() {
*x = ShutdownResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_kaspawalletd_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ShutdownResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ShutdownResponse) ProtoMessage() {}
func (x *ShutdownResponse) ProtoReflect() protoreflect.Message {
mi := &file_kaspawalletd_proto_msgTypes[9]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ShutdownResponse.ProtoReflect.Descriptor instead.
func (*ShutdownResponse) Descriptor() ([]byte, []int) {
return file_kaspawalletd_proto_rawDescGZIP(), []int{9}
}
var File_kaspawalletd_proto protoreflect.FileDescriptor
var file_kaspawalletd_proto_rawDesc = []byte{
0x0a, 0x12, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x22, 0x13, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e,
0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4c, 0x0a, 0x12, 0x47, 0x65, 0x74,
0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x1c, 0x0a, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01,
0x28, 0x04, 0x52, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x18, 0x0a,
0x07, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07,
0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x22, 0x54, 0x0a, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74,
0x65, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61,
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64,
0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18,
0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x55, 0x0a,
0x21, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54,
0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x30, 0x0a, 0x13, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72,
0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x13, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x22, 0x1a, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69,
0x76, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x22, 0x35, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x41, 0x64,
0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a,
0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x34, 0x0a, 0x10, 0x42, 0x72, 0x6f, 0x61, 0x64,
0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x74,
0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
0x52, 0x0b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x27, 0x0a,
0x11, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x04, 0x74, 0x78, 0x49, 0x44, 0x22, 0x11, 0x0a, 0x0f, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f,
0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x12, 0x0a, 0x10, 0x53, 0x68, 0x75,
0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xe4, 0x02,
0x0a, 0x0c, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x12, 0x37,
0x0a, 0x0a, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x2e, 0x47,
0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x13, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x64, 0x0a, 0x19, 0x43, 0x72, 0x65, 0x61, 0x74,
0x65, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x73,
0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4c, 0x0a,
0x11, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65,
0x73, 0x73, 0x12, 0x19, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x41,
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e,
0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x08, 0x53,
0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x10, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f,
0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x53, 0x68, 0x75, 0x74,
0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x34,
0x0a, 0x09, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x12, 0x11, 0x2e, 0x42, 0x72,
0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12,
0x2e, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x00, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x6e, 0x65, 0x74, 0x2f, 0x6b, 0x61, 0x73, 0x70,
0x61, 0x64, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c,
0x65, 0x74, 0x2f, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
}
var (
file_kaspawalletd_proto_rawDescOnce sync.Once
file_kaspawalletd_proto_rawDescData = file_kaspawalletd_proto_rawDesc
)
func file_kaspawalletd_proto_rawDescGZIP() []byte {
file_kaspawalletd_proto_rawDescOnce.Do(func() {
file_kaspawalletd_proto_rawDescData = protoimpl.X.CompressGZIP(file_kaspawalletd_proto_rawDescData)
})
return file_kaspawalletd_proto_rawDescData
}
var file_kaspawalletd_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
var file_kaspawalletd_proto_goTypes = []interface{}{
(*GetBalanceRequest)(nil), // 0: GetBalanceRequest
(*GetBalanceResponse)(nil), // 1: GetBalanceResponse
(*CreateUnsignedTransactionRequest)(nil), // 2: CreateUnsignedTransactionRequest
(*CreateUnsignedTransactionResponse)(nil), // 3: CreateUnsignedTransactionResponse
(*GetReceiveAddressRequest)(nil), // 4: GetReceiveAddressRequest
(*GetReceiveAddressResponse)(nil), // 5: GetReceiveAddressResponse
(*BroadcastRequest)(nil), // 6: BroadcastRequest
(*BroadcastResponse)(nil), // 7: BroadcastResponse
(*ShutdownRequest)(nil), // 8: ShutdownRequest
(*ShutdownResponse)(nil), // 9: ShutdownResponse
}
var file_kaspawalletd_proto_depIdxs = []int32{
0, // 0: kaspawalletd.GetBalance:input_type -> GetBalanceRequest
2, // 1: kaspawalletd.CreateUnsignedTransaction:input_type -> CreateUnsignedTransactionRequest
4, // 2: kaspawalletd.GetReceiveAddress:input_type -> GetReceiveAddressRequest
8, // 3: kaspawalletd.Shutdown:input_type -> ShutdownRequest
6, // 4: kaspawalletd.Broadcast:input_type -> BroadcastRequest
1, // 5: kaspawalletd.GetBalance:output_type -> GetBalanceResponse
3, // 6: kaspawalletd.CreateUnsignedTransaction:output_type -> CreateUnsignedTransactionResponse
5, // 7: kaspawalletd.GetReceiveAddress:output_type -> GetReceiveAddressResponse
9, // 8: kaspawalletd.Shutdown:output_type -> ShutdownResponse
7, // 9: kaspawalletd.Broadcast:output_type -> BroadcastResponse
5, // [5:10] is the sub-list for method output_type
0, // [0:5] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_kaspawalletd_proto_init() }
func file_kaspawalletd_proto_init() {
if File_kaspawalletd_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_kaspawalletd_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetBalanceRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_kaspawalletd_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetBalanceResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_kaspawalletd_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CreateUnsignedTransactionRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_kaspawalletd_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CreateUnsignedTransactionResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_kaspawalletd_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetReceiveAddressRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_kaspawalletd_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetReceiveAddressResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_kaspawalletd_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BroadcastRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_kaspawalletd_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BroadcastResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_kaspawalletd_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ShutdownRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_kaspawalletd_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ShutdownResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_kaspawalletd_proto_rawDesc,
NumEnums: 0,
NumMessages: 10,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_kaspawalletd_proto_goTypes,
DependencyIndexes: file_kaspawalletd_proto_depIdxs,
MessageInfos: file_kaspawalletd_proto_msgTypes,
}.Build()
File_kaspawalletd_proto = out.File
file_kaspawalletd_proto_rawDesc = nil
file_kaspawalletd_proto_goTypes = nil
file_kaspawalletd_proto_depIdxs = nil
}

View File

@@ -1,49 +0,0 @@
syntax = "proto3";
option go_package = "github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb";
service kaspawalletd {
rpc GetBalance (GetBalanceRequest) returns (GetBalanceResponse) {}
rpc CreateUnsignedTransaction (CreateUnsignedTransactionRequest) returns (CreateUnsignedTransactionResponse) {}
rpc GetReceiveAddress (GetReceiveAddressRequest) returns (GetReceiveAddressResponse) {}
rpc Shutdown (ShutdownRequest) returns (ShutdownResponse) {}
rpc Broadcast (BroadcastRequest) returns (BroadcastResponse) {}
}
message GetBalanceRequest {
}
message GetBalanceResponse {
uint64 available = 1;
uint64 pending = 2;
}
message CreateUnsignedTransactionRequest {
string address = 1;
uint64 amount = 2;
}
message CreateUnsignedTransactionResponse {
bytes unsignedTransaction = 1;
}
message GetReceiveAddressRequest {
}
message GetReceiveAddressResponse {
string address = 1;
}
message BroadcastRequest {
bytes transaction = 1;
}
message BroadcastResponse {
string txID = 1;
}
message ShutdownRequest {
}
message ShutdownResponse {
}

View File

@@ -1,234 +0,0 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
package pb
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion6
// KaspawalletdClient is the client API for Kaspawalletd service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type KaspawalletdClient interface {
GetBalance(ctx context.Context, in *GetBalanceRequest, opts ...grpc.CallOption) (*GetBalanceResponse, error)
CreateUnsignedTransaction(ctx context.Context, in *CreateUnsignedTransactionRequest, opts ...grpc.CallOption) (*CreateUnsignedTransactionResponse, error)
GetReceiveAddress(ctx context.Context, in *GetReceiveAddressRequest, opts ...grpc.CallOption) (*GetReceiveAddressResponse, error)
Shutdown(ctx context.Context, in *ShutdownRequest, opts ...grpc.CallOption) (*ShutdownResponse, error)
Broadcast(ctx context.Context, in *BroadcastRequest, opts ...grpc.CallOption) (*BroadcastResponse, error)
}
type kaspawalletdClient struct {
cc grpc.ClientConnInterface
}
func NewKaspawalletdClient(cc grpc.ClientConnInterface) KaspawalletdClient {
return &kaspawalletdClient{cc}
}
func (c *kaspawalletdClient) GetBalance(ctx context.Context, in *GetBalanceRequest, opts ...grpc.CallOption) (*GetBalanceResponse, error) {
out := new(GetBalanceResponse)
err := c.cc.Invoke(ctx, "/kaspawalletd/GetBalance", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *kaspawalletdClient) CreateUnsignedTransaction(ctx context.Context, in *CreateUnsignedTransactionRequest, opts ...grpc.CallOption) (*CreateUnsignedTransactionResponse, error) {
out := new(CreateUnsignedTransactionResponse)
err := c.cc.Invoke(ctx, "/kaspawalletd/CreateUnsignedTransaction", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *kaspawalletdClient) GetReceiveAddress(ctx context.Context, in *GetReceiveAddressRequest, opts ...grpc.CallOption) (*GetReceiveAddressResponse, error) {
out := new(GetReceiveAddressResponse)
err := c.cc.Invoke(ctx, "/kaspawalletd/GetReceiveAddress", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *kaspawalletdClient) Shutdown(ctx context.Context, in *ShutdownRequest, opts ...grpc.CallOption) (*ShutdownResponse, error) {
out := new(ShutdownResponse)
err := c.cc.Invoke(ctx, "/kaspawalletd/Shutdown", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *kaspawalletdClient) Broadcast(ctx context.Context, in *BroadcastRequest, opts ...grpc.CallOption) (*BroadcastResponse, error) {
out := new(BroadcastResponse)
err := c.cc.Invoke(ctx, "/kaspawalletd/Broadcast", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// KaspawalletdServer is the server API for Kaspawalletd service.
// All implementations must embed UnimplementedKaspawalletdServer
// for forward compatibility
type KaspawalletdServer interface {
GetBalance(context.Context, *GetBalanceRequest) (*GetBalanceResponse, error)
CreateUnsignedTransaction(context.Context, *CreateUnsignedTransactionRequest) (*CreateUnsignedTransactionResponse, error)
GetReceiveAddress(context.Context, *GetReceiveAddressRequest) (*GetReceiveAddressResponse, error)
Shutdown(context.Context, *ShutdownRequest) (*ShutdownResponse, error)
Broadcast(context.Context, *BroadcastRequest) (*BroadcastResponse, error)
mustEmbedUnimplementedKaspawalletdServer()
}
// UnimplementedKaspawalletdServer must be embedded to have forward compatible implementations.
type UnimplementedKaspawalletdServer struct {
}
func (*UnimplementedKaspawalletdServer) GetBalance(context.Context, *GetBalanceRequest) (*GetBalanceResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetBalance not implemented")
}
func (*UnimplementedKaspawalletdServer) CreateUnsignedTransaction(context.Context, *CreateUnsignedTransactionRequest) (*CreateUnsignedTransactionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateUnsignedTransaction not implemented")
}
func (*UnimplementedKaspawalletdServer) GetReceiveAddress(context.Context, *GetReceiveAddressRequest) (*GetReceiveAddressResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetReceiveAddress not implemented")
}
func (*UnimplementedKaspawalletdServer) Shutdown(context.Context, *ShutdownRequest) (*ShutdownResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Shutdown not implemented")
}
func (*UnimplementedKaspawalletdServer) Broadcast(context.Context, *BroadcastRequest) (*BroadcastResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Broadcast not implemented")
}
func (*UnimplementedKaspawalletdServer) mustEmbedUnimplementedKaspawalletdServer() {}
func RegisterKaspawalletdServer(s *grpc.Server, srv KaspawalletdServer) {
s.RegisterService(&_Kaspawalletd_serviceDesc, srv)
}
func _Kaspawalletd_GetBalance_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetBalanceRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(KaspawalletdServer).GetBalance(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/kaspawalletd/GetBalance",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(KaspawalletdServer).GetBalance(ctx, req.(*GetBalanceRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Kaspawalletd_CreateUnsignedTransaction_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateUnsignedTransactionRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(KaspawalletdServer).CreateUnsignedTransaction(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/kaspawalletd/CreateUnsignedTransaction",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(KaspawalletdServer).CreateUnsignedTransaction(ctx, req.(*CreateUnsignedTransactionRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Kaspawalletd_GetReceiveAddress_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetReceiveAddressRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(KaspawalletdServer).GetReceiveAddress(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/kaspawalletd/GetReceiveAddress",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(KaspawalletdServer).GetReceiveAddress(ctx, req.(*GetReceiveAddressRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Kaspawalletd_Shutdown_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ShutdownRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(KaspawalletdServer).Shutdown(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/kaspawalletd/Shutdown",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(KaspawalletdServer).Shutdown(ctx, req.(*ShutdownRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Kaspawalletd_Broadcast_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(BroadcastRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(KaspawalletdServer).Broadcast(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/kaspawalletd/Broadcast",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(KaspawalletdServer).Broadcast(ctx, req.(*BroadcastRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Kaspawalletd_serviceDesc = grpc.ServiceDesc{
ServiceName: "kaspawalletd",
HandlerType: (*KaspawalletdServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetBalance",
Handler: _Kaspawalletd_GetBalance_Handler,
},
{
MethodName: "CreateUnsignedTransaction",
Handler: _Kaspawalletd_CreateUnsignedTransaction_Handler,
},
{
MethodName: "GetReceiveAddress",
Handler: _Kaspawalletd_GetReceiveAddress_Handler,
},
{
MethodName: "Shutdown",
Handler: _Kaspawalletd_Shutdown_Handler,
},
{
MethodName: "Broadcast",
Handler: _Kaspawalletd_Broadcast_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "kaspawalletd.proto",
}

View File

@@ -1,83 +0,0 @@
package server
import (
"context"
"fmt"
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
"github.com/kaspanet/kaspad/util"
"github.com/pkg/errors"
)
func (s *server) changeAddress() (util.Address, error) {
err := s.keysFile.SetLastUsedInternalIndex(s.keysFile.LastUsedInternalIndex() + 1)
if err != nil {
return nil, err
}
err = s.keysFile.Save()
if err != nil {
return nil, err
}
walletAddr := &walletAddress{
index: s.keysFile.LastUsedInternalIndex(),
cosignerIndex: s.keysFile.CosignerIndex,
keyChain: internalKeychain,
}
path := s.walletAddressPath(walletAddr)
return libkaspawallet.Address(s.params, s.keysFile.ExtendedPublicKeys, s.keysFile.MinimumSignatures, path, s.keysFile.ECDSA)
}
func (s *server) GetReceiveAddress(_ context.Context, request *pb.GetReceiveAddressRequest) (*pb.GetReceiveAddressResponse, error) {
s.lock.Lock()
defer s.lock.Unlock()
if !s.isSynced() {
return nil, errors.New("server is not synced")
}
err := s.keysFile.SetLastUsedExternalIndex(s.keysFile.LastUsedExternalIndex() + 1)
if err != nil {
return nil, err
}
err = s.keysFile.Save()
if err != nil {
return nil, err
}
walletAddr := &walletAddress{
index: s.keysFile.LastUsedExternalIndex(),
cosignerIndex: s.keysFile.CosignerIndex,
keyChain: externalKeychain,
}
path := s.walletAddressPath(walletAddr)
address, err := libkaspawallet.Address(s.params, s.keysFile.ExtendedPublicKeys, s.keysFile.MinimumSignatures, path, s.keysFile.ECDSA)
if err != nil {
return nil, err
}
return &pb.GetReceiveAddressResponse{Address: address.String()}, nil
}
func (s *server) walletAddressString(wAddr *walletAddress) (string, error) {
path := s.walletAddressPath(wAddr)
addr, err := libkaspawallet.Address(s.params, s.keysFile.ExtendedPublicKeys, s.keysFile.MinimumSignatures, path, s.keysFile.ECDSA)
if err != nil {
return "", err
}
return addr.String(), nil
}
func (s *server) walletAddressPath(wAddr *walletAddress) string {
if s.isMultisig() {
return fmt.Sprintf("m/%d/%d/%d", wAddr.cosignerIndex, wAddr.keyChain, wAddr.index)
}
return fmt.Sprintf("m/%d/%d", wAddr.keyChain, wAddr.index)
}
func (s *server) isMultisig() bool {
return len(s.keysFile.ExtendedPublicKeys) > 1
}

View File

@@ -1,37 +0,0 @@
package server
import (
"context"
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
)
func (s *server) GetBalance(_ context.Context, _ *pb.GetBalanceRequest) (*pb.GetBalanceResponse, error) {
s.lock.RLock()
defer s.lock.RUnlock()
dagInfo, err := s.rpcClient.GetBlockDAGInfo()
if err != nil {
return nil, err
}
var availableBalance, pendingBalance uint64
for _, entry := range s.utxos {
if isUTXOSpendable(entry, dagInfo.VirtualDAAScore, s.params.BlockCoinbaseMaturity) {
availableBalance += entry.UTXOEntry.Amount()
} else {
pendingBalance += entry.UTXOEntry.Amount()
}
}
return &pb.GetBalanceResponse{
Available: availableBalance,
Pending: pendingBalance,
}, nil
}
func isUTXOSpendable(entry *walletUTXO, virtualDAAScore uint64, coinbaseMaturity uint64) bool {
if !entry.UTXOEntry.IsCoinbase() {
return true
}
return entry.UTXOEntry.BlockDAAScore()+coinbaseMaturity < virtualDAAScore
}

View File

@@ -1,33 +0,0 @@
package server
import (
"context"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/infrastructure/network/rpcclient"
"github.com/pkg/errors"
)
func (s *server) Broadcast(_ context.Context, request *pb.BroadcastRequest) (*pb.BroadcastResponse, error) {
tx, err := libkaspawallet.ExtractTransaction(request.Transaction, s.keysFile.ECDSA)
if err != nil {
return nil, err
}
txID, err := sendTransaction(s.rpcClient, tx)
if err != nil {
return nil, err
}
return &pb.BroadcastResponse{TxID: txID}, nil
}
func sendTransaction(client *rpcclient.RPCClient, tx *externalapi.DomainTransaction) (string, error) {
submitTransactionResponse, err := client.SubmitTransaction(appmessage.DomainTransactionToRPCTransaction(tx))
if err != nil {
return "", errors.Wrapf(err, "error submitting transaction")
}
return submitTransactionResponse.TransactionID, nil
}

View File

@@ -1,15 +0,0 @@
package server
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
type walletUTXO struct {
Outpoint *externalapi.DomainOutpoint
UTXOEntry externalapi.UTXOEntry
address *walletAddress
}
type walletAddress struct {
index uint32
cosignerIndex uint32
keyChain uint8
}

View File

@@ -1,95 +0,0 @@
package server
import (
"context"
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
"github.com/kaspanet/kaspad/util"
"github.com/pkg/errors"
)
func (s *server) CreateUnsignedTransaction(_ context.Context, request *pb.CreateUnsignedTransactionRequest) (*pb.CreateUnsignedTransactionResponse, error) {
s.lock.Lock()
defer s.lock.Unlock()
if !s.isSynced() {
return nil, errors.New("server is not synced")
}
err := s.refreshExistingUTXOs()
if err != nil {
return nil, err
}
toAddress, err := util.DecodeAddress(request.Address, s.params.Prefix)
if err != nil {
return nil, err
}
// TODO: Implement a better fee estimation mechanism
const feePerInput = 1000
selectedUTXOs, changeSompi, err := s.selectUTXOs(request.Amount, feePerInput)
if err != nil {
return nil, err
}
changeAddress, err := s.changeAddress()
if err != nil {
return nil, err
}
unsignedTransaction, err := libkaspawallet.CreateUnsignedTransaction(s.keysFile.ExtendedPublicKeys,
s.keysFile.MinimumSignatures,
[]*libkaspawallet.Payment{{
Address: toAddress,
Amount: request.Amount,
}, {
Address: changeAddress,
Amount: changeSompi,
}}, selectedUTXOs)
if err != nil {
return nil, err
}
return &pb.CreateUnsignedTransactionResponse{UnsignedTransaction: unsignedTransaction}, nil
}
func (s *server) selectUTXOs(spendAmount uint64, feePerInput uint64) (
selectedUTXOs []*libkaspawallet.UTXO, changeSompi uint64, err error) {
selectedUTXOs = []*libkaspawallet.UTXO{}
totalValue := uint64(0)
dagInfo, err := s.rpcClient.GetBlockDAGInfo()
if err != nil {
return nil, 0, err
}
for _, utxo := range s.utxos {
if !isUTXOSpendable(utxo, dagInfo.VirtualDAAScore, s.params.BlockCoinbaseMaturity) {
continue
}
selectedUTXOs = append(selectedUTXOs, &libkaspawallet.UTXO{
Outpoint: utxo.Outpoint,
UTXOEntry: utxo.UTXOEntry,
DerivationPath: s.walletAddressPath(utxo.address),
})
totalValue += utxo.UTXOEntry.Amount()
fee := feePerInput * uint64(len(selectedUTXOs))
totalSpend := spendAmount + fee
if totalValue >= totalSpend {
break
}
}
fee := feePerInput * uint64(len(selectedUTXOs))
totalSpend := spendAmount + fee
if totalValue < totalSpend {
return nil, 0, errors.Errorf("Insufficient funds for send: %f required, while only %f available",
float64(totalSpend)/util.SompiPerKaspa, float64(totalValue)/util.SompiPerKaspa)
}
return selectedUTXOs, totalValue - totalSpend, nil
}

View File

@@ -1,47 +0,0 @@
package server
import (
"fmt"
"os"
"path/filepath"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/util/panics"
)
var (
backendLog = logger.NewBackend()
log = backendLog.Logger("KSWD")
spawn = panics.GoroutineWrapperFunc(log)
defaultAppDir = util.AppDir("kaspawallet", false)
defaultLogFile = filepath.Join(defaultAppDir, "daemon.log")
defaultErrLogFile = filepath.Join(defaultAppDir, "daemon_err.log")
)
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)
os.Exit(1)
}
err = backendLog.AddLogFile(errLogFile, logger.LevelWarn)
if err != nil {
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)
}
}

View File

@@ -1,15 +0,0 @@
package server
import (
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/infrastructure/network/rpcclient"
)
func connectToRPC(params *dagconfig.Params, rpcServer string) (*rpcclient.RPCClient, error) {
rpcAddress, err := params.NormalizeRPCServerAddress(rpcServer)
if err != nil {
return nil, err
}
return rpcclient.NewRPCClient(rpcAddress)
}

View File

@@ -1,109 +0,0 @@
package server
import (
"fmt"
"net"
"os"
"sync"
"time"
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
"github.com/kaspanet/kaspad/cmd/kaspawallet/keys"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/infrastructure/network/rpcclient"
"github.com/kaspanet/kaspad/infrastructure/os/signal"
"github.com/kaspanet/kaspad/util/panics"
"github.com/pkg/errors"
"google.golang.org/grpc"
)
type server struct {
pb.UnimplementedKaspawalletdServer
rpcClient *rpcclient.RPCClient
params *dagconfig.Params
lock sync.RWMutex
utxos map[externalapi.DomainOutpoint]*walletUTXO
nextSyncStartIndex uint32
keysFile *keys.File
shutdown chan struct{}
}
// Start starts the kaspawalletd server
func Start(params *dagconfig.Params, listen, rpcServer string, keysFilePath string) error {
initLog(defaultLogFile, defaultErrLogFile)
defer panics.HandlePanic(log, "MAIN", nil)
interrupt := signal.InterruptListener()
listener, err := net.Listen("tcp", listen)
if err != nil {
return (errors.Wrapf(err, "Error listening to tcp at %s", listen))
}
log.Infof("Listening on %s", listen)
rpcClient, err := connectToRPC(params, rpcServer)
if err != nil {
return (errors.Wrapf(err, "Error connecting to RPC server %s", rpcServer))
}
keysFile, err := keys.ReadKeysFile(params, keysFilePath)
if err != nil {
return (errors.Wrapf(err, "Error connecting to RPC server %s", rpcServer))
}
serverInstance := &server{
rpcClient: rpcClient,
params: params,
utxos: make(map[externalapi.DomainOutpoint]*walletUTXO),
nextSyncStartIndex: 0,
keysFile: keysFile,
shutdown: make(chan struct{}),
}
spawn("serverInstance.sync", func() {
err := serverInstance.sync()
if err != nil {
printErrorAndExit(errors.Wrap(err, "error syncing the wallet"))
}
})
grpcServer := grpc.NewServer()
pb.RegisterKaspawalletdServer(grpcServer, serverInstance)
spawn("grpcServer.Serve", func() {
err := grpcServer.Serve(listener)
if err != nil {
printErrorAndExit(errors.Wrap(err, "Error serving gRPC"))
}
})
select {
case <-serverInstance.shutdown:
case <-interrupt:
const stopTimeout = 2 * time.Second
stopChan := make(chan interface{})
spawn("gRPCServer.Stop", func() {
grpcServer.GracefulStop()
close(stopChan)
})
select {
case <-stopChan:
case <-time.After(stopTimeout):
log.Warnf("Could not gracefully stop: timed out after %s", stopTimeout)
grpcServer.Stop()
}
}
return nil
}
func printErrorAndExit(err error) {
fmt.Fprintf(os.Stderr, "%+v\n", err)
os.Exit(1)
}

View File

@@ -1,13 +0,0 @@
package server
import (
"context"
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
)
func (s *server) Shutdown(ctx context.Context, request *pb.ShutdownRequest) (*pb.ShutdownResponse, error) {
s.lock.Lock()
defer s.lock.Unlock()
close(s.shutdown)
return &pb.ShutdownResponse{}, nil
}

View File

@@ -1,263 +0,0 @@
package server
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/pkg/errors"
"time"
)
const (
// externalKeychain is the key chain that is used to create receive addresses
externalKeychain = 0
// internalKeychain is used to create change addresses
internalKeychain = 1
)
var keyChains = []uint8{externalKeychain, internalKeychain}
type walletAddressSet map[string]*walletAddress
func (was walletAddressSet) strings() []string {
addresses := make([]string, 0, len(was))
for addr := range was {
addresses = append(addresses, addr)
}
return addresses
}
func (s *server) sync() error {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for range ticker.C {
err := s.collectUTXOsFromRecentAddresses()
if err != nil {
return err
}
err = s.collectUTXOsFromFarAddresses()
if err != nil {
return err
}
err = s.refreshExistingUTXOsWithLock()
if err != nil {
return err
}
}
return nil
}
const numIndexesToQuery = 100
// addressesToQuery scans the addresses in the given range. Because
// each cosigner in a multisig has its own unique path for generating
// addresses it goes over all the cosigners and add their addresses
// for each key chain.
func (s *server) addressesToQuery(start, end uint32) (walletAddressSet, error) {
addresses := make(walletAddressSet)
for index := start; index < end; index++ {
for cosignerIndex := uint32(0); cosignerIndex < uint32(len(s.keysFile.ExtendedPublicKeys)); cosignerIndex++ {
for _, keychain := range keyChains {
address := &walletAddress{
index: index,
cosignerIndex: cosignerIndex,
keyChain: keychain,
}
addressString, err := s.walletAddressString(address)
if err != nil {
return nil, err
}
addresses[addressString] = address
}
}
}
return addresses, nil
}
// collectUTXOsFromFarAddresses collects numIndexesToQuery UTXOs
// from the last point it stopped in the previous call.
func (s *server) collectUTXOsFromFarAddresses() error {
s.lock.Lock()
defer s.lock.Unlock()
err := s.collectUTXOs(s.nextSyncStartIndex, s.nextSyncStartIndex+numIndexesToQuery)
if err != nil {
return err
}
s.nextSyncStartIndex += numIndexesToQuery
return nil
}
func (s *server) maxUsedIndex() uint32 {
s.lock.RLock()
defer s.lock.RUnlock()
maxUsedIndex := s.keysFile.LastUsedExternalIndex()
if s.keysFile.LastUsedInternalIndex() > maxUsedIndex {
maxUsedIndex = s.keysFile.LastUsedInternalIndex()
}
return maxUsedIndex
}
// collectUTXOsFromRecentAddresses collects UTXOs from used addresses until
// the address with the index of the last used address + 1000.
// collectUTXOsFromRecentAddresses scans addresses in batches of numIndexesToQuery,
// and releases the lock between scans.
func (s *server) collectUTXOsFromRecentAddresses() error {
maxUsedIndex := s.maxUsedIndex()
for i := uint32(0); i < maxUsedIndex+1000; i += numIndexesToQuery {
err := s.collectUTXOsWithLock(i, i+numIndexesToQuery)
if err != nil {
return err
}
}
return nil
}
func (s *server) collectUTXOsWithLock(start, end uint32) error {
s.lock.Lock()
defer s.lock.Unlock()
return s.collectUTXOs(start, end)
}
func (s *server) collectUTXOs(start, end uint32) error {
addressSet, err := s.addressesToQuery(start, end)
if err != nil {
return err
}
getUTXOsByAddressesResponse, err := s.rpcClient.GetUTXOsByAddresses(addressSet.strings())
if err != nil {
return err
}
err = s.updateLastUsedIndexes(addressSet, getUTXOsByAddressesResponse)
if err != nil {
return err
}
err = s.updateUTXOs(addressSet, getUTXOsByAddressesResponse)
if err != nil {
return err
}
return nil
}
func (s *server) updateUTXOs(addressSet walletAddressSet,
getUTXOsByAddressesResponse *appmessage.GetUTXOsByAddressesResponseMessage) error {
for _, entry := range getUTXOsByAddressesResponse.Entries {
err := s.addEntryToUTXOSet(entry, addressSet)
if err != nil {
return err
}
}
return nil
}
func (s *server) updateLastUsedIndexes(addressSet walletAddressSet,
getUTXOsByAddressesResponse *appmessage.GetUTXOsByAddressesResponseMessage) error {
lastUsedExternalIndex := s.keysFile.LastUsedExternalIndex()
lastUsedInternalIndex := s.keysFile.LastUsedInternalIndex()
for _, entry := range getUTXOsByAddressesResponse.Entries {
walletAddress, ok := addressSet[entry.Address]
if !ok {
return errors.Errorf("Got result from address %s even though it wasn't requested", entry.Address)
}
if walletAddress.cosignerIndex != s.keysFile.CosignerIndex {
continue
}
if walletAddress.keyChain == externalKeychain {
if walletAddress.index > lastUsedExternalIndex {
lastUsedExternalIndex = walletAddress.index
}
continue
}
if walletAddress.index > lastUsedInternalIndex {
lastUsedInternalIndex = walletAddress.index
}
}
err := s.keysFile.SetLastUsedExternalIndex(lastUsedExternalIndex)
if err != nil {
return err
}
return s.keysFile.SetLastUsedInternalIndex(lastUsedInternalIndex)
}
func (s *server) refreshExistingUTXOsWithLock() error {
s.lock.Lock()
defer s.lock.Unlock()
return s.refreshExistingUTXOs()
}
func (s *server) addEntryToUTXOSet(entry *appmessage.UTXOsByAddressesEntry, addressSet walletAddressSet) error {
outpoint, err := appmessage.RPCOutpointToDomainOutpoint(entry.Outpoint)
if err != nil {
return err
}
utxoEntry, err := appmessage.RPCUTXOEntryToUTXOEntry(entry.UTXOEntry)
if err != nil {
return err
}
address, ok := addressSet[entry.Address]
if !ok {
return errors.Errorf("Got result from address %s even though it wasn't requested", entry.Address)
}
s.utxos[*outpoint] = &walletUTXO{
Outpoint: outpoint,
UTXOEntry: utxoEntry,
address: address,
}
return nil
}
func (s *server) refreshExistingUTXOs() error {
addressSet := make(walletAddressSet, len(s.utxos))
for _, utxo := range s.utxos {
addressString, err := s.walletAddressString(utxo.address)
if err != nil {
return err
}
addressSet[addressString] = utxo.address
}
getUTXOsByAddressesResponse, err := s.rpcClient.GetUTXOsByAddresses(addressSet.strings())
if err != nil {
return err
}
s.utxos = make(map[externalapi.DomainOutpoint]*walletUTXO, len(getUTXOsByAddressesResponse.Entries))
for _, entry := range getUTXOsByAddressesResponse.Entries {
err := s.addEntryToUTXOSet(entry, addressSet)
if err != nil {
return err
}
}
return nil
}
func (s *server) isSynced() bool {
return s.nextSyncStartIndex > s.keysFile.LastUsedInternalIndex() && s.nextSyncStartIndex > s.keysFile.LastUsedExternalIndex()
}

View File

@@ -1,73 +0,0 @@
package main
import (
"bufio"
"fmt"
"github.com/kaspanet/kaspad/cmd/kaspawallet/keys"
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
"github.com/kaspanet/kaspad/cmd/kaspawallet/utils"
"os"
"github.com/pkg/errors"
)
func dumpUnencryptedData(conf *dumpUnencryptedDataConfig) error {
if !conf.Yes {
err := confirmDump()
if err != nil {
return err
}
}
keysFile, err := keys.ReadKeysFile(conf.NetParams(), conf.KeysFile)
if err != nil {
return err
}
mnemonics, err := keysFile.DecryptMnemonics(conf.Password)
if err != nil {
return err
}
mnemonicPublicKeys := make(map[string]struct{})
for i, mnemonic := range mnemonics {
fmt.Printf("Mnemonic #%d:\n%s\n\n", i+1, mnemonic)
publicKey, err := libkaspawallet.MasterPublicKeyFromMnemonic(conf.NetParams(), mnemonic, len(keysFile.ExtendedPublicKeys) > 1)
if err != nil {
return err
}
mnemonicPublicKeys[publicKey] = struct{}{}
}
i := 1
for _, extendedPublicKey := range keysFile.ExtendedPublicKeys {
if _, exists := mnemonicPublicKeys[extendedPublicKey]; exists {
continue
}
fmt.Printf("Extended Public key #%d:\n%s\n\n", i, extendedPublicKey)
i++
}
fmt.Printf("Minimum number of signatures: %d\n", keysFile.MinimumSignatures)
return nil
}
func confirmDump() error {
reader := bufio.NewReader(os.Stdin)
fmt.Printf("This operation will print your unencrypted keys on the screen. Anyone that sees this information " +
"will be able to steal your funds. Are you sure you want to proceed (y/N)? ")
line, err := utils.ReadLine(reader)
if err != nil {
return err
}
fmt.Println()
if string(line) != "y" {
return errors.Errorf("Dump aborted by user")
}
return nil
}

View File

@@ -1,118 +0,0 @@
package keys
import (
"bufio"
"crypto/rand"
"crypto/subtle"
"fmt"
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
"github.com/kaspanet/kaspad/cmd/kaspawallet/utils"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/pkg/errors"
"github.com/tyler-smith/go-bip39"
"os"
)
// CreateMnemonics generates `numKeys` number of mnemonics.
func CreateMnemonics(params *dagconfig.Params, numKeys uint32, cmdLinePassword string, isMultisig bool) (encryptedPrivateKeys []*EncryptedMnemonic, extendedPublicKeys []string, err error) {
mnemonics := make([]string, numKeys)
for i := uint32(0); i < numKeys; i++ {
var err error
mnemonics[i], err = libkaspawallet.CreateMnemonic()
if err != nil {
return nil, nil, err
}
}
return encryptedMnemonicExtendedPublicKeyPairs(params, mnemonics, cmdLinePassword, isMultisig)
}
// ImportMnemonics imports a `numKeys` of mnemonics.
func ImportMnemonics(params *dagconfig.Params, numKeys uint32, cmdLinePassword string, isMultisig bool) (encryptedPrivateKeys []*EncryptedMnemonic, extendedPublicKeys []string, err error) {
mnemonics := make([]string, numKeys)
for i := uint32(0); i < numKeys; i++ {
fmt.Printf("Enter mnemonic #%d here:\n", i+1)
reader := bufio.NewReader(os.Stdin)
mnemonic, err := utils.ReadLine(reader)
if err != nil {
return nil, nil, err
}
if !bip39.IsMnemonicValid(string(mnemonic)) {
return nil, nil, errors.Errorf("mnemonic is invalid")
}
mnemonics[i] = string(mnemonic)
}
return encryptedMnemonicExtendedPublicKeyPairs(params, mnemonics, cmdLinePassword, isMultisig)
}
func encryptedMnemonicExtendedPublicKeyPairs(params *dagconfig.Params, mnemonics []string, cmdLinePassword string, isMultisig bool) (
encryptedPrivateKeys []*EncryptedMnemonic, extendedPublicKeys []string, err error) {
password := []byte(cmdLinePassword)
if len(password) == 0 {
password = getPassword("Enter password for the key file:")
confirmPassword := getPassword("Confirm password:")
if subtle.ConstantTimeCompare(password, confirmPassword) != 1 {
return nil, nil, errors.New("Passwords are not identical")
}
}
encryptedPrivateKeys = make([]*EncryptedMnemonic, 0, len(mnemonics))
for _, mnemonic := range mnemonics {
extendedPublicKey, err := libkaspawallet.MasterPublicKeyFromMnemonic(params, mnemonic, isMultisig)
if err != nil {
return nil, nil, err
}
extendedPublicKeys = append(extendedPublicKeys, extendedPublicKey)
encryptedPrivateKey, err := encryptMnemonic(mnemonic, password)
if err != nil {
return nil, nil, err
}
encryptedPrivateKeys = append(encryptedPrivateKeys, encryptedPrivateKey)
}
return encryptedPrivateKeys, extendedPublicKeys, nil
}
func generateSalt() ([]byte, error) {
salt := make([]byte, 16)
_, err := rand.Read(salt)
if err != nil {
return nil, err
}
return salt, nil
}
func encryptMnemonic(mnemonic string, password []byte) (*EncryptedMnemonic, error) {
mnemonicBytes := []byte(mnemonic)
salt, err := generateSalt()
if err != nil {
return nil, err
}
aead, err := getAEAD(password, salt)
if err != nil {
return nil, err
}
// Select a random nonce, and leave capacity for the ciphertext.
nonce := make([]byte, aead.NonceSize(), aead.NonceSize()+len(mnemonicBytes)+aead.Overhead())
if _, err := rand.Read(nonce); err != nil {
return nil, err
}
// Encrypt the message and append the ciphertext to the nonce.
cipher := aead.Seal(nonce, nonce, []byte(mnemonicBytes), nil)
return &EncryptedMnemonic{
cipher: cipher,
salt: salt,
}, nil
}

View File

@@ -1,41 +0,0 @@
package keys
import (
"fmt"
"golang.org/x/term"
"os"
"os/signal"
"syscall"
)
// getPassword was adapted from https://gist.github.com/jlinoff/e8e26b4ffa38d379c7f1891fd174a6d0#file-getpassword2-go
func getPassword(prompt string) []byte {
// Get the initial state of the terminal.
initialTermState, e1 := term.GetState(int(syscall.Stdin))
if e1 != nil {
panic(e1)
}
// Restore it in the event of an interrupt.
// CITATION: Konstantin Shaposhnikov - https://groups.google.com/forum/#!topic/golang-nuts/kTVAbtee9UA
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, os.Kill)
go func() {
<-c
_ = term.Restore(int(syscall.Stdin), initialTermState)
os.Exit(1)
}()
// Now get the password.
fmt.Print(prompt)
p, err := term.ReadPassword(int(syscall.Stdin))
fmt.Println()
if err != nil {
panic(err)
}
// Stop looking for ^C on the channel.
signal.Stop(c)
return p
}

View File

@@ -1,306 +0,0 @@
package keys
import (
"bufio"
"crypto/cipher"
"encoding/hex"
"encoding/json"
"fmt"
"github.com/kaspanet/kaspad/cmd/kaspawallet/utils"
"os"
"path/filepath"
"runtime"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/util"
"github.com/pkg/errors"
"golang.org/x/crypto/argon2"
"golang.org/x/crypto/chacha20poly1305"
)
var (
defaultAppDir = util.AppDir("kaspawallet", false)
)
func defaultKeysFile(netParams *dagconfig.Params) string {
return filepath.Join(defaultAppDir, netParams.Name, "keys.json")
}
type encryptedPrivateKeyJSON struct {
Cipher string `json:"cipher"`
Salt string `json:"salt"`
}
type keysFileJSON struct {
EncryptedPrivateKeys []*encryptedPrivateKeyJSON `json:"encryptedMnemonics"`
ExtendedPublicKeys []string `json:"publicKeys"`
MinimumSignatures uint32 `json:"minimumSignatures"`
CosignerIndex uint32 `json:"cosignerIndex"`
LastUsedExternalIndex uint32 `json:"lastUsedExternalIndex"`
LastUsedInternalIndex uint32 `json:"lastUsedInternalIndex"`
ECDSA bool `json:"ecdsa"`
}
// EncryptedMnemonic represents an encrypted mnemonic
type EncryptedMnemonic struct {
cipher []byte
salt []byte
}
// File holds all the data related to the wallet keys
type File struct {
EncryptedMnemonics []*EncryptedMnemonic
ExtendedPublicKeys []string
MinimumSignatures uint32
CosignerIndex uint32
lastUsedExternalIndex uint32
lastUsedInternalIndex uint32
ECDSA bool
path string
}
func (d *File) toJSON() *keysFileJSON {
encryptedPrivateKeysJSON := make([]*encryptedPrivateKeyJSON, len(d.EncryptedMnemonics))
for i, encryptedPrivateKey := range d.EncryptedMnemonics {
encryptedPrivateKeysJSON[i] = &encryptedPrivateKeyJSON{
Cipher: hex.EncodeToString(encryptedPrivateKey.cipher),
Salt: hex.EncodeToString(encryptedPrivateKey.salt),
}
}
return &keysFileJSON{
EncryptedPrivateKeys: encryptedPrivateKeysJSON,
ExtendedPublicKeys: d.ExtendedPublicKeys,
MinimumSignatures: d.MinimumSignatures,
ECDSA: d.ECDSA,
CosignerIndex: d.CosignerIndex,
LastUsedExternalIndex: d.lastUsedExternalIndex,
LastUsedInternalIndex: d.lastUsedInternalIndex,
}
}
func (d *File) fromJSON(fileJSON *keysFileJSON) error {
d.MinimumSignatures = fileJSON.MinimumSignatures
d.ECDSA = fileJSON.ECDSA
d.ExtendedPublicKeys = fileJSON.ExtendedPublicKeys
d.CosignerIndex = fileJSON.CosignerIndex
d.lastUsedExternalIndex = fileJSON.LastUsedExternalIndex
d.lastUsedInternalIndex = fileJSON.LastUsedInternalIndex
d.EncryptedMnemonics = make([]*EncryptedMnemonic, len(fileJSON.EncryptedPrivateKeys))
for i, encryptedPrivateKeyJSON := range fileJSON.EncryptedPrivateKeys {
cipher, err := hex.DecodeString(encryptedPrivateKeyJSON.Cipher)
if err != nil {
return err
}
salt, err := hex.DecodeString(encryptedPrivateKeyJSON.Salt)
if err != nil {
return err
}
d.EncryptedMnemonics[i] = &EncryptedMnemonic{
cipher: cipher,
salt: salt,
}
}
return nil
}
// SetPath sets the path where the file is saved to.
func (d *File) SetPath(params *dagconfig.Params, path string, forceOverride bool) error {
if path == "" {
path = defaultKeysFile(params)
}
if !forceOverride {
exists, err := pathExists(path)
if err != nil {
return err
}
if exists {
reader := bufio.NewReader(os.Stdin)
fmt.Printf("The file %s already exists. Are you sure you want to override it (type 'y' to approve)? ", d.path)
line, err := utils.ReadLine(reader)
if err != nil {
return err
}
if string(line) != "y" {
return errors.Errorf("aborted setting the file path to %s", path)
}
}
}
d.path = path
return nil
}
// Path returns the file path.
func (d *File) Path() string {
return d.path
}
// SetLastUsedExternalIndex sets the last used index in the external key
// chain, and saves the file with the updated data.
func (d *File) SetLastUsedExternalIndex(index uint32) error {
if d.lastUsedExternalIndex == index {
return nil
}
d.lastUsedExternalIndex = index
return d.Save()
}
// LastUsedExternalIndex returns the last used index in the external key
// chain and saves the file with the updated data.
func (d *File) LastUsedExternalIndex() uint32 {
return d.lastUsedExternalIndex
}
// SetLastUsedInternalIndex sets the last used index in the internal key chain, and saves the file.
func (d *File) SetLastUsedInternalIndex(index uint32) error {
if d.lastUsedInternalIndex == index {
return nil
}
d.lastUsedInternalIndex = index
return d.Save()
}
// LastUsedInternalIndex returns the last used index in the internal key chain
func (d *File) LastUsedInternalIndex() uint32 {
return d.lastUsedInternalIndex
}
// DecryptMnemonics asks the user to enter the password for the private keys and
// returns the decrypted private keys.
func (d *File) DecryptMnemonics(cmdLinePassword string) ([]string, error) {
password := []byte(cmdLinePassword)
if len(password) == 0 {
password = getPassword("Password:")
}
privateKeys := make([]string, len(d.EncryptedMnemonics))
for i, encryptedPrivateKey := range d.EncryptedMnemonics {
var err error
privateKeys[i], err = decryptMnemonic(encryptedPrivateKey, password)
if err != nil {
return nil, err
}
}
return privateKeys, nil
}
// ReadKeysFile returns the data related to the keys file
func ReadKeysFile(netParams *dagconfig.Params, path string) (*File, error) {
if path == "" {
path = defaultKeysFile(netParams)
}
file, err := os.Open(path)
if err != nil {
return nil, err
}
decoder := json.NewDecoder(file)
decoder.DisallowUnknownFields()
decodedFile := &keysFileJSON{}
err = decoder.Decode(&decodedFile)
if err != nil {
return nil, err
}
keysFile := &File{
path: path,
}
err = keysFile.fromJSON(decodedFile)
if err != nil {
return nil, err
}
return keysFile, nil
}
func createFileDirectoryIfDoesntExist(path string) error {
dir := filepath.Dir(path)
exists, err := pathExists(dir)
if err != nil {
return err
}
if exists {
return nil
}
return os.MkdirAll(dir, 0700)
}
func pathExists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
// Save writes the file contents to the disk.
func (d *File) Save() error {
if d.path == "" {
return errors.New("cannot save a file with uninitialized path")
}
err := createFileDirectoryIfDoesntExist(d.path)
if err != nil {
return err
}
file, err := os.OpenFile(d.path, os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return err
}
defer file.Close()
encoder := json.NewEncoder(file)
err = encoder.Encode(d.toJSON())
if err != nil {
return err
}
return nil
}
func getAEAD(password, salt []byte) (cipher.AEAD, error) {
key := argon2.IDKey(password, salt, 1, 64*1024, uint8(runtime.NumCPU()), 32)
return chacha20poly1305.NewX(key)
}
func decryptMnemonic(encryptedPrivateKey *EncryptedMnemonic, password []byte) (string, error) {
aead, err := getAEAD(password, encryptedPrivateKey.salt)
if err != nil {
return "", err
}
if len(encryptedPrivateKey.cipher) < aead.NonceSize() {
return "", errors.New("ciphertext too short")
}
// Split nonce and ciphertext.
nonce, ciphertext := encryptedPrivateKey.cipher[:aead.NonceSize()], encryptedPrivateKey.cipher[aead.NonceSize():]
// Decrypt the message and check it wasn't tampered with.
decrypted, err := aead.Open(nil, nonce, ciphertext, nil)
if err != nil {
return "", err
}
return string(decrypted), nil
}

View File

@@ -1,49 +0,0 @@
// Copyright (c) 2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
// AUTOGENERATED by genalphabet.go; do not edit.
package base58
const (
// alphabet is the modified base58 alphabet used by Bitcoin.
alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
alphabetIdx0 = '1'
)
var b58 = [256]byte{
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 0, 1, 2, 3, 4, 5, 6,
7, 8, 255, 255, 255, 255, 255, 255,
255, 9, 10, 11, 12, 13, 14, 15,
16, 255, 17, 18, 19, 20, 21, 255,
22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 255, 255, 255, 255, 255,
255, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 255, 44, 45, 46,
47, 48, 49, 50, 51, 52, 53, 54,
55, 56, 57, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
}

View File

@@ -1,75 +0,0 @@
// Copyright (c) 2013-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package base58
import (
"math/big"
)
//go:generate go run genalphabet.go
var bigRadix = big.NewInt(58)
var bigZero = big.NewInt(0)
// Decode decodes a modified base58 string to a byte slice.
func Decode(b string) []byte {
answer := big.NewInt(0)
j := big.NewInt(1)
scratch := new(big.Int)
for i := len(b) - 1; i >= 0; i-- {
tmp := b58[b[i]]
if tmp == 255 {
return []byte("")
}
scratch.SetInt64(int64(tmp))
scratch.Mul(j, scratch)
answer.Add(answer, scratch)
j.Mul(j, bigRadix)
}
tmpval := answer.Bytes()
var numZeros int
for numZeros = 0; numZeros < len(b); numZeros++ {
if b[numZeros] != alphabetIdx0 {
break
}
}
flen := numZeros + len(tmpval)
val := make([]byte, flen)
copy(val[numZeros:], tmpval)
return val
}
// Encode encodes a byte slice to a modified base58 string.
func Encode(b []byte) string {
x := new(big.Int)
x.SetBytes(b)
answer := make([]byte, 0, len(b)*136/100)
for x.Cmp(bigZero) > 0 {
mod := new(big.Int)
x.DivMod(x, bigRadix, mod)
answer = append(answer, alphabet[mod.Int64()])
}
// leading zero bytes
for _, i := range b {
if i != 0 {
break
}
answer = append(answer, alphabetIdx0)
}
// reverse
alen := len(answer)
for i := 0; i < alen/2; i++ {
answer[i], answer[alen-1-i] = answer[alen-1-i], answer[i]
}
return string(answer)
}

View File

@@ -1,98 +0,0 @@
// Copyright (c) 2013-2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package base58_test
import (
"bytes"
"encoding/hex"
"testing"
"github.com/btcsuite/btcutil/base58"
)
var stringTests = []struct {
in string
out string
}{
{"", ""},
{" ", "Z"},
{"-", "n"},
{"0", "q"},
{"1", "r"},
{"-1", "4SU"},
{"11", "4k8"},
{"abc", "ZiCa"},
{"1234598760", "3mJr7AoUXx2Wqd"},
{"abcdefghijklmnopqrstuvwxyz", "3yxU3u1igY8WkgtjK92fbJQCd4BZiiT1v25f"},
{"00000000000000000000000000000000000000000000000000000000000000", "3sN2THZeE9Eh9eYrwkvZqNstbHGvrxSAM7gXUXvyFQP8XvQLUqNCS27icwUeDT7ckHm4FUHM2mTVh1vbLmk7y"},
}
var invalidStringTests = []struct {
in string
out string
}{
{"0", ""},
{"O", ""},
{"I", ""},
{"l", ""},
{"3mJr0", ""},
{"O3yxU", ""},
{"3sNI", ""},
{"4kl8", ""},
{"0OIl", ""},
{"!@#$%^&*()-_=+~`", ""},
}
var hexTests = []struct {
in string
out string
}{
{"61", "2g"},
{"626262", "a3gV"},
{"636363", "aPEr"},
{"73696d706c792061206c6f6e6720737472696e67", "2cFupjhnEsSn59qHXstmK2ffpLv2"},
{"00eb15231dfceb60925886b67d065299925915aeb172c06647", "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L"},
{"516b6fcd0f", "ABnLTmg"},
{"bf4f89001e670274dd", "3SEo3LWLoPntC"},
{"572e4794", "3EFU7m"},
{"ecac89cad93923c02321", "EJDM8drfXA6uyA"},
{"10c8511e", "Rt5zm"},
{"00000000000000000000", "1111111111"},
}
func TestBase58(t *testing.T) {
// Encode tests
for x, test := range stringTests {
tmp := []byte(test.in)
if res := base58.Encode(tmp); res != test.out {
t.Errorf("Encode test #%d failed: got: %s want: %s",
x, res, test.out)
continue
}
}
// Decode tests
for x, test := range hexTests {
b, err := hex.DecodeString(test.in)
if err != nil {
t.Errorf("hex.DecodeString failed failed #%d: got: %s", x, test.in)
continue
}
if res := base58.Decode(test.out); !bytes.Equal(res, b) {
t.Errorf("Decode test #%d failed: got: %q want: %q",
x, res, test.in)
continue
}
}
// Decode with invalid input
for x, test := range invalidStringTests {
if res := base58.Decode(test.in); string(res) != test.out {
t.Errorf("Decode invalidString test #%d failed: got: %q want: %q",
x, res, test.out)
continue
}
}
}

View File

@@ -1,35 +0,0 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package base58_test
import (
"bytes"
"testing"
"github.com/btcsuite/btcutil/base58"
)
func BenchmarkBase58Encode(b *testing.B) {
b.StopTimer()
data := bytes.Repeat([]byte{0xff}, 5000)
b.SetBytes(int64(len(data)))
b.StartTimer()
for i := 0; i < b.N; i++ {
base58.Encode(data)
}
}
func BenchmarkBase58Decode(b *testing.B) {
b.StopTimer()
data := bytes.Repeat([]byte{0xff}, 5000)
encoded := base58.Encode(data)
b.SetBytes(int64(len(encoded)))
b.StartTimer()
for i := 0; i < b.N; i++ {
base58.Decode(encoded)
}
}

View File

@@ -1,52 +0,0 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package base58
import (
"crypto/sha256"
"errors"
)
// ErrChecksum indicates that the checksum of a check-encoded string does not verify against
// the checksum.
var ErrChecksum = errors.New("checksum error")
// ErrInvalidFormat indicates that the check-encoded string has an invalid format.
var ErrInvalidFormat = errors.New("invalid format: version and/or checksum bytes missing")
// checksum: first four bytes of sha256^2
func checksum(input []byte) (cksum [4]byte) {
h := sha256.Sum256(input)
h2 := sha256.Sum256(h[:])
copy(cksum[:], h2[:4])
return
}
// CheckEncode prepends a version byte and appends a four byte checksum.
func CheckEncode(input []byte, version byte) string {
b := make([]byte, 0, 1+len(input)+4)
b = append(b, version)
b = append(b, input[:]...)
cksum := checksum(b)
b = append(b, cksum[:]...)
return Encode(b)
}
// CheckDecode decodes a string that was encoded with CheckEncode and verifies the checksum.
func CheckDecode(input string) (result []byte, version byte, err error) {
decoded := Decode(input)
if len(decoded) < 5 {
return nil, 0, ErrInvalidFormat
}
version = decoded[0]
var cksum [4]byte
copy(cksum[:], decoded[len(decoded)-4:])
if checksum(decoded[:len(decoded)-4]) != cksum {
return nil, 0, ErrChecksum
}
payload := decoded[1 : len(decoded)-4]
result = append(result, payload...)
return
}

View File

@@ -1,66 +0,0 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package base58_test
import (
"testing"
"github.com/btcsuite/btcutil/base58"
)
var checkEncodingStringTests = []struct {
version byte
in string
out string
}{
{20, "", "3MNQE1X"},
{20, " ", "B2Kr6dBE"},
{20, "-", "B3jv1Aft"},
{20, "0", "B482yuaX"},
{20, "1", "B4CmeGAC"},
{20, "-1", "mM7eUf6kB"},
{20, "11", "mP7BMTDVH"},
{20, "abc", "4QiVtDjUdeq"},
{20, "1234598760", "ZmNb8uQn5zvnUohNCEPP"},
{20, "abcdefghijklmnopqrstuvwxyz", "K2RYDcKfupxwXdWhSAxQPCeiULntKm63UXyx5MvEH2"},
{20, "00000000000000000000000000000000000000000000000000000000000000", "bi1EWXwJay2udZVxLJozuTb8Meg4W9c6xnmJaRDjg6pri5MBAxb9XwrpQXbtnqEoRV5U2pixnFfwyXC8tRAVC8XxnjK"},
}
func TestBase58Check(t *testing.T) {
for x, test := range checkEncodingStringTests {
// test encoding
if res := base58.CheckEncode([]byte(test.in), test.version); res != test.out {
t.Errorf("CheckEncode test #%d failed: got %s, want: %s", x, res, test.out)
}
// test decoding
res, version, err := base58.CheckDecode(test.out)
if err != nil {
t.Errorf("CheckDecode test #%d failed with err: %v", x, err)
} else if version != test.version {
t.Errorf("CheckDecode test #%d failed: got version: %d want: %d", x, version, test.version)
} else if string(res) != test.in {
t.Errorf("CheckDecode test #%d failed: got: %s want: %s", x, res, test.in)
}
}
// test the two decoding failure cases
// case 1: checksum error
_, _, err := base58.CheckDecode("3MNQE1Y")
if err != base58.ErrChecksum {
t.Error("Checkdecode test failed, expected ErrChecksum")
}
// case 2: invalid formats (string lengths below 5 mean the version byte and/or the checksum
// bytes are missing).
testString := ""
for len := 0; len < 4; len++ {
// make a string of length `len`
_, _, err = base58.CheckDecode(testString)
if err != base58.ErrInvalidFormat {
t.Error("Checkdecode test failed, expected ErrInvalidFormat")
}
}
}

View File

@@ -1,17 +0,0 @@
#!/bin/sh
# This script uses gocov to generate a test coverage report.
# The gocov tool my be obtained with the following command:
# go get github.com/axw/gocov/gocov
#
# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH.
# Check for gocov.
type gocov >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo >&2 "This script requires the gocov tool."
echo >&2 "You may obtain it with the following command:"
echo >&2 "go get github.com/axw/gocov/gocov"
exit 1
fi
gocov test | gocov report

View File

@@ -1,29 +0,0 @@
// Copyright (c) 2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
/*
Package base58 provides an API for working with modified base58 and Base58Check
encodings.
Modified Base58 Encoding
Standard base58 encoding is similar to standard base64 encoding except, as the
name implies, it uses a 58 character alphabet which results in an alphanumeric
string and allows some characters which are problematic for humans to be
excluded. Due to this, there can be various base58 alphabets.
The modified base58 alphabet used by Bitcoin, and hence this package, omits the
0, O, I, and l characters that look the same in many fonts and are therefore
hard to humans to distinguish.
Base58Check Encoding Scheme
The Base58Check encoding scheme is primarily used for Bitcoin addresses at the
time of this writing, however it can be used to generically encode arbitrary
byte arrays into human-readable strings along with a version byte that can be
used to differentiate the same payload. For Bitcoin addresses, the extra
version is used to differentiate the network of otherwise identical public keys
which helps prevent using an address intended for one network on another.
*/
package base58

View File

@@ -1,71 +0,0 @@
// Copyright (c) 2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package base58_test
import (
"fmt"
"github.com/btcsuite/btcutil/base58"
)
// This example demonstrates how to decode modified base58 encoded data.
func ExampleDecode() {
// Decode example modified base58 encoded data.
encoded := "25JnwSn7XKfNQ"
decoded := base58.Decode(encoded)
// Show the decoded data.
fmt.Println("Decoded Data:", string(decoded))
// Output:
// Decoded Data: Test data
}
// This example demonstrates how to encode data using the modified base58
// encoding scheme.
func ExampleEncode() {
// Encode example data with the modified base58 encoding scheme.
data := []byte("Test data")
encoded := base58.Encode(data)
// Show the encoded data.
fmt.Println("Encoded Data:", encoded)
// Output:
// Encoded Data: 25JnwSn7XKfNQ
}
// This example demonstrates how to decode Base58Check encoded data.
func ExampleCheckDecode() {
// Decode an example Base58Check encoded data.
encoded := "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
decoded, version, err := base58.CheckDecode(encoded)
if err != nil {
fmt.Println(err)
return
}
// Show the decoded data.
fmt.Printf("Decoded data: %x\n", decoded)
fmt.Println("Version Byte:", version)
// Output:
// Decoded data: 62e907b15cbf27d5425399ebf6f0fb50ebb88f18
// Version Byte: 0
}
// This example demonstrates how to encode data using the Base58Check encoding
// scheme.
func ExampleCheckEncode() {
// Encode example data with the Base58Check encoding scheme.
data := []byte("Test data")
encoded := base58.CheckEncode(data, 0)
// Show the encoded data.
fmt.Println("Encoded Data:", encoded)
// Output:
// Encoded Data: 182iP79GRURMp7oMHDU
}

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