mirror of
https://github.com/kaspanet/kaspad.git
synced 2026-03-05 00:28:51 +00:00
Compare commits
6 Commits
testsCheck
...
v0.9.1-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e457503aea | ||
|
|
c6c63c7ff9 | ||
|
|
dd91ffb7d8 | ||
|
|
921ca19b42 | ||
|
|
98c2dc8189 | ||
|
|
37654156a6 |
5
.github/workflows/go.yml
vendored
5
.github/workflows/go.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ ubuntu-16.04, macos-10.15, windows-2019 ]
|
os: [ ubuntu-16.04, macos-10.15 ]
|
||||||
name: Testing on on ${{ matrix.os }}
|
name: Testing on on ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
@@ -63,9 +63,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
go-version: 1.16
|
go-version: 1.16
|
||||||
|
|
||||||
- name: Delete the stability tests from coverage
|
|
||||||
run: rm -r stability-tests
|
|
||||||
|
|
||||||
- name: Create coverage file
|
- name: Create coverage file
|
||||||
run: go test -v -covermode=atomic -coverpkg=./... -coverprofile coverage.txt ./...
|
run: go test -v -covermode=atomic -coverpkg=./... -coverprofile coverage.txt ./...
|
||||||
|
|
||||||
|
|||||||
@@ -56,15 +56,13 @@ $ kaspad
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Discord
|
## 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
|
## Issue Tracker
|
||||||
|
|
||||||
The [integrated github issue tracker](https://github.com/kaspanet/kaspad/issues)
|
The [integrated github issue tracker](https://github.com/kaspanet/kaspad/issues)
|
||||||
is used for this project.
|
is used for this project.
|
||||||
|
|
||||||
Issue priorities may be seen at https://github.com/orgs/kaspanet/projects/4
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
The [documentation](https://github.com/kaspanet/docs) is a work-in-progress
|
The [documentation](https://github.com/kaspanet/docs) is a work-in-progress
|
||||||
|
|||||||
27
app/app.go
27
app/app.go
@@ -85,6 +85,12 @@ func (app *kaspadApp) main(startedChan chan<- struct{}) error {
|
|||||||
profiling.Start(app.cfg.Profile, log)
|
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.
|
// Return now if an interrupt signal was triggered.
|
||||||
if signal.InterruptRequested(interrupt) {
|
if signal.InterruptRequested(interrupt) {
|
||||||
return nil
|
return nil
|
||||||
@@ -157,9 +163,15 @@ func (app *kaspadApp) main(startedChan chan<- struct{}) error {
|
|||||||
return nil
|
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.
|
// dbPath returns the path to the block database given a database type.
|
||||||
func databasePath(cfg *config.Config) string {
|
func databasePath(cfg *config.Config) string {
|
||||||
return filepath.Join(cfg.AppDir, "data")
|
return filepath.Join(cfg.DataDir, "db")
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeDatabase(cfg *config.Config) error {
|
func removeDatabase(cfg *config.Config) error {
|
||||||
@@ -169,17 +181,6 @@ func removeDatabase(cfg *config.Config) error {
|
|||||||
|
|
||||||
func openDB(cfg *config.Config) (database.Database, error) {
|
func openDB(cfg *config.Config) (database.Database, error) {
|
||||||
dbPath := databasePath(cfg)
|
dbPath := databasePath(cfg)
|
||||||
|
|
||||||
err := checkDatabaseVersion(dbPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Infof("Loading database from '%s'", dbPath)
|
log.Infof("Loading database from '%s'", dbPath)
|
||||||
db, err := ldb.NewLevelDB(dbPath, leveldbCacheSizeMiB)
|
return ldb.NewLevelDB(dbPath, leveldbCacheSizeMiB)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return db, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package appmessage
|
|||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
|
"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/utils/utxo"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
@@ -84,6 +83,7 @@ func DomainTransactionToMsgTx(domainTransaction *externalapi.DomainTransaction)
|
|||||||
LockTime: domainTransaction.LockTime,
|
LockTime: domainTransaction.LockTime,
|
||||||
SubnetworkID: domainTransaction.SubnetworkID,
|
SubnetworkID: domainTransaction.SubnetworkID,
|
||||||
Gas: domainTransaction.Gas,
|
Gas: domainTransaction.Gas,
|
||||||
|
PayloadHash: domainTransaction.PayloadHash,
|
||||||
Payload: domainTransaction.Payload,
|
Payload: domainTransaction.Payload,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,6 +133,7 @@ func MsgTxToDomainTransaction(msgTx *MsgTx) *externalapi.DomainTransaction {
|
|||||||
LockTime: msgTx.LockTime,
|
LockTime: msgTx.LockTime,
|
||||||
SubnetworkID: msgTx.SubnetworkID,
|
SubnetworkID: msgTx.SubnetworkID,
|
||||||
Gas: msgTx.Gas,
|
Gas: msgTx.Gas,
|
||||||
|
PayloadHash: msgTx.PayloadHash,
|
||||||
Payload: payload,
|
Payload: payload,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,10 +164,14 @@ func outpointToDomainOutpoint(outpoint *Outpoint) *externalapi.DomainOutpoint {
|
|||||||
func RPCTransactionToDomainTransaction(rpcTransaction *RPCTransaction) (*externalapi.DomainTransaction, error) {
|
func RPCTransactionToDomainTransaction(rpcTransaction *RPCTransaction) (*externalapi.DomainTransaction, error) {
|
||||||
inputs := make([]*externalapi.DomainTransactionInput, len(rpcTransaction.Inputs))
|
inputs := make([]*externalapi.DomainTransactionInput, len(rpcTransaction.Inputs))
|
||||||
for i, input := range rpcTransaction.Inputs {
|
for i, input := range rpcTransaction.Inputs {
|
||||||
previousOutpoint, err := RPCOutpointToDomainOutpoint(input.PreviousOutpoint)
|
transactionID, err := transactionid.FromString(input.PreviousOutpoint.TransactionID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
previousOutpoint := &externalapi.DomainOutpoint{
|
||||||
|
TransactionID: *transactionID,
|
||||||
|
Index: input.PreviousOutpoint.Index,
|
||||||
|
}
|
||||||
signatureScript, err := hex.DecodeString(input.SignatureScript)
|
signatureScript, err := hex.DecodeString(input.SignatureScript)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -193,6 +198,10 @@ func RPCTransactionToDomainTransaction(rpcTransaction *RPCTransaction) (*externa
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
payloadHash, err := externalapi.NewDomainHashFromString(rpcTransaction.PayloadHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
payload, err := hex.DecodeString(rpcTransaction.Payload)
|
payload, err := hex.DecodeString(rpcTransaction.Payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -205,40 +214,11 @@ func RPCTransactionToDomainTransaction(rpcTransaction *RPCTransaction) (*externa
|
|||||||
LockTime: rpcTransaction.LockTime,
|
LockTime: rpcTransaction.LockTime,
|
||||||
SubnetworkID: *subnetworkID,
|
SubnetworkID: *subnetworkID,
|
||||||
Gas: rpcTransaction.LockTime,
|
Gas: rpcTransaction.LockTime,
|
||||||
|
PayloadHash: *payloadHash,
|
||||||
Payload: payload,
|
Payload: payload,
|
||||||
}, nil
|
}, 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
|
// DomainTransactionToRPCTransaction converts DomainTransactions to RPCTransactions
|
||||||
func DomainTransactionToRPCTransaction(transaction *externalapi.DomainTransaction) *RPCTransaction {
|
func DomainTransactionToRPCTransaction(transaction *externalapi.DomainTransaction) *RPCTransaction {
|
||||||
inputs := make([]*RPCTransactionInput, len(transaction.Inputs))
|
inputs := make([]*RPCTransactionInput, len(transaction.Inputs))
|
||||||
@@ -264,6 +244,7 @@ func DomainTransactionToRPCTransaction(transaction *externalapi.DomainTransactio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
subnetworkID := transaction.SubnetworkID.String()
|
subnetworkID := transaction.SubnetworkID.String()
|
||||||
|
payloadHash := transaction.PayloadHash.String()
|
||||||
payload := hex.EncodeToString(transaction.Payload)
|
payload := hex.EncodeToString(transaction.Payload)
|
||||||
return &RPCTransaction{
|
return &RPCTransaction{
|
||||||
Version: transaction.Version,
|
Version: transaction.Version,
|
||||||
@@ -272,6 +253,7 @@ func DomainTransactionToRPCTransaction(transaction *externalapi.DomainTransactio
|
|||||||
LockTime: transaction.LockTime,
|
LockTime: transaction.LockTime,
|
||||||
SubnetworkID: subnetworkID,
|
SubnetworkID: subnetworkID,
|
||||||
Gas: transaction.LockTime,
|
Gas: transaction.LockTime,
|
||||||
|
PayloadHash: payloadHash,
|
||||||
Payload: payload,
|
Payload: payload,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -283,27 +265,22 @@ func OutpointAndUTXOEntryPairsToDomainOutpointAndUTXOEntryPairs(
|
|||||||
|
|
||||||
domainOutpointAndUTXOEntryPairs := make([]*externalapi.OutpointAndUTXOEntryPair, len(outpointAndUTXOEntryPairs))
|
domainOutpointAndUTXOEntryPairs := make([]*externalapi.OutpointAndUTXOEntryPair, len(outpointAndUTXOEntryPairs))
|
||||||
for i, outpointAndUTXOEntryPair := range 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
|
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
|
// DomainOutpointAndUTXOEntryPairsToOutpointAndUTXOEntryPairs converts
|
||||||
// domain OutpointAndUTXOEntryPairs to OutpointAndUTXOEntryPairs
|
// domain OutpointAndUTXOEntryPairs to OutpointAndUTXOEntryPairs
|
||||||
func DomainOutpointAndUTXOEntryPairsToOutpointAndUTXOEntryPairs(
|
func DomainOutpointAndUTXOEntryPairsToOutpointAndUTXOEntryPairs(
|
||||||
@@ -320,76 +297,9 @@ func DomainOutpointAndUTXOEntryPairsToOutpointAndUTXOEntryPairs(
|
|||||||
Amount: outpointAndUTXOEntryPair.UTXOEntry.Amount(),
|
Amount: outpointAndUTXOEntryPair.UTXOEntry.Amount(),
|
||||||
ScriptPublicKey: outpointAndUTXOEntryPair.UTXOEntry.ScriptPublicKey(),
|
ScriptPublicKey: outpointAndUTXOEntryPair.UTXOEntry.ScriptPublicKey(),
|
||||||
IsCoinbase: outpointAndUTXOEntryPair.UTXOEntry.IsCoinbase(),
|
IsCoinbase: outpointAndUTXOEntryPair.UTXOEntry.IsCoinbase(),
|
||||||
BlockDAAScore: outpointAndUTXOEntryPair.UTXOEntry.BlockDAAScore(),
|
BlockBlueScore: outpointAndUTXOEntryPair.UTXOEntry.BlockBlueScore(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return domainOutpointAndUTXOEntryPairs
|
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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -137,11 +137,6 @@ const (
|
|||||||
CmdPruningPointUTXOSetOverrideNotificationMessage
|
CmdPruningPointUTXOSetOverrideNotificationMessage
|
||||||
CmdStopNotifyingPruningPointUTXOSetOverrideRequestMessage
|
CmdStopNotifyingPruningPointUTXOSetOverrideRequestMessage
|
||||||
CmdStopNotifyingPruningPointUTXOSetOverrideResponseMessage
|
CmdStopNotifyingPruningPointUTXOSetOverrideResponseMessage
|
||||||
CmdEstimateNetworkHashesPerSecondRequestMessage
|
|
||||||
CmdEstimateNetworkHashesPerSecondResponseMessage
|
|
||||||
CmdNotifyVirtualDaaScoreChangedRequestMessage
|
|
||||||
CmdNotifyVirtualDaaScoreChangedResponseMessage
|
|
||||||
CmdVirtualDaaScoreChangedNotificationMessage
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ProtocolMessageCommandToString maps all MessageCommands to their string representation
|
// ProtocolMessageCommandToString maps all MessageCommands to their string representation
|
||||||
@@ -253,11 +248,6 @@ var RPCMessageCommandToString = map[MessageCommand]string{
|
|||||||
CmdPruningPointUTXOSetOverrideNotificationMessage: "PruningPointUTXOSetOverrideNotification",
|
CmdPruningPointUTXOSetOverrideNotificationMessage: "PruningPointUTXOSetOverrideNotification",
|
||||||
CmdStopNotifyingPruningPointUTXOSetOverrideRequestMessage: "StopNotifyingPruningPointUTXOSetOverrideRequest",
|
CmdStopNotifyingPruningPointUTXOSetOverrideRequestMessage: "StopNotifyingPruningPointUTXOSetOverrideRequest",
|
||||||
CmdStopNotifyingPruningPointUTXOSetOverrideResponseMessage: "StopNotifyingPruningPointUTXOSetOverrideResponse",
|
CmdStopNotifyingPruningPointUTXOSetOverrideResponseMessage: "StopNotifyingPruningPointUTXOSetOverrideResponse",
|
||||||
CmdEstimateNetworkHashesPerSecondRequestMessage: "EstimateNetworkHashesPerSecondRequest",
|
|
||||||
CmdEstimateNetworkHashesPerSecondResponseMessage: "EstimateNetworkHashesPerSecondResponse",
|
|
||||||
CmdNotifyVirtualDaaScoreChangedRequestMessage: "NotifyVirtualDaaScoreChangedRequest",
|
|
||||||
CmdNotifyVirtualDaaScoreChangedResponseMessage: "NotifyVirtualDaaScoreChangedResponse",
|
|
||||||
CmdVirtualDaaScoreChangedNotificationMessage: "VirtualDaaScoreChangedNotification",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message is an interface that describes a kaspa message. A type that
|
// Message is an interface that describes a kaspa message. A type that
|
||||||
|
|||||||
@@ -15,6 +15,19 @@ import (
|
|||||||
// backing array multiple times.
|
// backing array multiple times.
|
||||||
const defaultTransactionAlloc = 2048
|
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
|
// TxLoc holds locator data for the offset and length of where a transaction is
|
||||||
// located within a MsgBlock data buffer.
|
// located within a MsgBlock data buffer.
|
||||||
type TxLoc struct {
|
type TxLoc struct {
|
||||||
|
|||||||
@@ -31,6 +31,6 @@ type OutpointAndUTXOEntryPair struct {
|
|||||||
type UTXOEntry struct {
|
type UTXOEntry struct {
|
||||||
Amount uint64
|
Amount uint64
|
||||||
ScriptPublicKey *externalapi.ScriptPublicKey
|
ScriptPublicKey *externalapi.ScriptPublicKey
|
||||||
BlockDAAScore uint64
|
BlockBlueScore uint64
|
||||||
IsCoinbase bool
|
IsCoinbase bool
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package appmessage
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||||
@@ -132,6 +133,7 @@ type MsgTx struct {
|
|||||||
LockTime uint64
|
LockTime uint64
|
||||||
SubnetworkID externalapi.DomainSubnetworkID
|
SubnetworkID externalapi.DomainSubnetworkID
|
||||||
Gas uint64
|
Gas uint64
|
||||||
|
PayloadHash externalapi.DomainHash
|
||||||
Payload []byte
|
Payload []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,6 +179,7 @@ func (msg *MsgTx) Copy() *MsgTx {
|
|||||||
LockTime: msg.LockTime,
|
LockTime: msg.LockTime,
|
||||||
SubnetworkID: msg.SubnetworkID,
|
SubnetworkID: msg.SubnetworkID,
|
||||||
Gas: msg.Gas,
|
Gas: msg.Gas,
|
||||||
|
PayloadHash: msg.PayloadHash,
|
||||||
}
|
}
|
||||||
|
|
||||||
if msg.Payload != nil {
|
if msg.Payload != nil {
|
||||||
@@ -277,12 +280,18 @@ func newMsgTx(version uint16, txIn []*TxIn, txOut []*TxOut, subnetworkID *extern
|
|||||||
txOut = make([]*TxOut, 0, defaultTxInOutAlloc)
|
txOut = make([]*TxOut, 0, defaultTxInOutAlloc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var payloadHash externalapi.DomainHash
|
||||||
|
if *subnetworkID != subnetworks.SubnetworkIDNative {
|
||||||
|
payloadHash = *hashes.PayloadHash(payload)
|
||||||
|
}
|
||||||
|
|
||||||
return &MsgTx{
|
return &MsgTx{
|
||||||
Version: version,
|
Version: version,
|
||||||
TxIn: txIn,
|
TxIn: txIn,
|
||||||
TxOut: txOut,
|
TxOut: txOut,
|
||||||
SubnetworkID: *subnetworkID,
|
SubnetworkID: *subnetworkID,
|
||||||
Gas: gas,
|
Gas: gas,
|
||||||
|
PayloadHash: payloadHash,
|
||||||
Payload: payload,
|
Payload: payload,
|
||||||
LockTime: lockTime,
|
LockTime: lockTime,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,8 +133,8 @@ func TestTx(t *testing.T) {
|
|||||||
|
|
||||||
// TestTxHash tests the ability to generate the hash of a transaction accurately.
|
// TestTxHash tests the ability to generate the hash of a transaction accurately.
|
||||||
func TestTxHashAndID(t *testing.T) {
|
func TestTxHashAndID(t *testing.T) {
|
||||||
txHash1Str := "93663e597f6c968d32d229002f76408edf30d6a0151ff679fc729812d8cb2acc"
|
txHash1Str := "4bee9ee495bd93a755de428376bd582a2bb6ec37c041753b711c0606d5745c13"
|
||||||
txID1Str := "24079c6d2bdf602fc389cc307349054937744a9c8dc0f07c023e6af0e949a4e7"
|
txID1Str := "f868bd20e816256b80eac976821be4589d24d21141bd1cec6e8005d0c16c6881"
|
||||||
wantTxID1, err := transactionid.FromString(txID1Str)
|
wantTxID1, err := transactionid.FromString(txID1Str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewTxIDFromStr: %v", err)
|
t.Fatalf("NewTxIDFromStr: %v", err)
|
||||||
@@ -185,14 +185,14 @@ func TestTxHashAndID(t *testing.T) {
|
|||||||
spew.Sprint(tx1ID), spew.Sprint(wantTxID1))
|
spew.Sprint(tx1ID), spew.Sprint(wantTxID1))
|
||||||
}
|
}
|
||||||
|
|
||||||
hash2Str := "8dafd1bec24527d8e3b443ceb0a3b92fffc0d60026317f890b2faf5e9afc177a"
|
hash2Str := "cb1bdb4a83d4885535fb3cceb5c96597b7df903db83f0ffcd779d703affd8efd"
|
||||||
wantHash2, err := externalapi.NewDomainHashFromString(hash2Str)
|
wantHash2, err := externalapi.NewDomainHashFromString(hash2Str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("NewTxIDFromStr: %v", err)
|
t.Errorf("NewTxIDFromStr: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
id2Str := "89ffb49474637502d9059af38b8a95fc2f0d3baef5c801d7a9b9c8830671b711"
|
id2Str := "ca080073d4ddf5b84443a0964af633f3c70a5b290fd3bc35a7e6f93fd33f9330"
|
||||||
wantID2, err := transactionid.FromString(id2Str)
|
wantID2, err := transactionid.FromString(id2Str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("NewTxIDFromStr: %v", err)
|
t.Errorf("NewTxIDFromStr: %v", err)
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func TestVersion(t *testing.T) {
|
|||||||
|
|
||||||
// Create version message data.
|
// Create version message data.
|
||||||
tcpAddrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 16111}
|
tcpAddrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 16111}
|
||||||
me := NewNetAddress(tcpAddrMe)
|
me := NewNetAddress(tcpAddrMe, SFNodeNetwork)
|
||||||
generatedID, err := id.GenerateID()
|
generatedID, err := id.GenerateID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("id.GenerateID: %s", err)
|
t.Fatalf("id.GenerateID: %s", err)
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ type NetAddress struct {
|
|||||||
// Last time the address was seen.
|
// Last time the address was seen.
|
||||||
Timestamp mstime.Time
|
Timestamp mstime.Time
|
||||||
|
|
||||||
|
// Bitfield which identifies the services supported by the address.
|
||||||
|
Services ServiceFlag
|
||||||
|
|
||||||
// IP address of the peer.
|
// IP address of the peer.
|
||||||
IP net.IP
|
IP net.IP
|
||||||
|
|
||||||
@@ -23,6 +26,17 @@ type NetAddress struct {
|
|||||||
Port uint16
|
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
|
// TCPAddress converts the NetAddress to *net.TCPAddr
|
||||||
func (na *NetAddress) TCPAddress() *net.TCPAddr {
|
func (na *NetAddress) TCPAddress() *net.TCPAddr {
|
||||||
return &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
|
// NewNetAddressIPPort returns a new NetAddress using the provided IP, port, and
|
||||||
// supported services with defaults for the remaining fields.
|
// supported services with defaults for the remaining fields.
|
||||||
func NewNetAddressIPPort(ip net.IP, port uint16) *NetAddress {
|
func NewNetAddressIPPort(ip net.IP, port uint16, services ServiceFlag) *NetAddress {
|
||||||
return NewNetAddressTimestamp(mstime.Now(), ip, port)
|
return NewNetAddressTimestamp(mstime.Now(), services, ip, port)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewNetAddressTimestamp returns a new NetAddress using the provided
|
// NewNetAddressTimestamp returns a new NetAddress using the provided
|
||||||
// timestamp, IP, port, and supported services. The timestamp is rounded to
|
// timestamp, IP, port, and supported services. The timestamp is rounded to
|
||||||
// single millisecond precision.
|
// single millisecond precision.
|
||||||
func NewNetAddressTimestamp(
|
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
|
// Limit the timestamp to one millisecond precision since the protocol
|
||||||
// doesn't support better.
|
// doesn't support better.
|
||||||
na := NetAddress{
|
na := NetAddress{
|
||||||
Timestamp: timestamp,
|
Timestamp: timestamp,
|
||||||
|
Services: services,
|
||||||
IP: ip,
|
IP: ip,
|
||||||
Port: port,
|
Port: port,
|
||||||
}
|
}
|
||||||
@@ -54,6 +69,6 @@ func NewNetAddressTimestamp(
|
|||||||
|
|
||||||
// NewNetAddress returns a new NetAddress using the provided TCP address and
|
// NewNetAddress returns a new NetAddress using the provided TCP address and
|
||||||
// supported services with defaults for the remaining fields.
|
// supported services with defaults for the remaining fields.
|
||||||
func NewNetAddress(addr *net.TCPAddr) *NetAddress {
|
func NewNetAddress(addr *net.TCPAddr, services ServiceFlag) *NetAddress {
|
||||||
return NewNetAddressIPPort(addr.IP, uint16(addr.Port))
|
return NewNetAddressIPPort(addr.IP, uint16(addr.Port), services)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ func TestNetAddress(t *testing.T) {
|
|||||||
port := 16111
|
port := 16111
|
||||||
|
|
||||||
// Test NewNetAddress.
|
// 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.
|
// Ensure we get the same ip, port, and services back out.
|
||||||
if !na.IP.Equal(ip) {
|
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,
|
t.Errorf("NetNetAddress: wrong port - got %v, want %v", na.Port,
|
||||||
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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,8 +4,8 @@ package appmessage
|
|||||||
// its respective RPC message
|
// its respective RPC message
|
||||||
type GetBlockRequestMessage struct {
|
type GetBlockRequestMessage struct {
|
||||||
baseMessage
|
baseMessage
|
||||||
Hash string
|
Hash string
|
||||||
IncludeTransactions bool
|
IncludeTransactionVerboseData bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
// 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
|
// NewGetBlockRequestMessage returns a instance of the message
|
||||||
func NewGetBlockRequestMessage(hash string, includeTransactions bool) *GetBlockRequestMessage {
|
func NewGetBlockRequestMessage(hash string, includeTransactionVerboseData bool) *GetBlockRequestMessage {
|
||||||
return &GetBlockRequestMessage{
|
return &GetBlockRequestMessage{
|
||||||
Hash: hash,
|
Hash: hash,
|
||||||
IncludeTransactions: includeTransactions,
|
IncludeTransactionVerboseData: includeTransactionVerboseData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ func NewGetBlockRequestMessage(hash string, includeTransactions bool) *GetBlockR
|
|||||||
// its respective RPC message
|
// its respective RPC message
|
||||||
type GetBlockResponseMessage struct {
|
type GetBlockResponseMessage struct {
|
||||||
baseMessage
|
baseMessage
|
||||||
Block *RPCBlock
|
BlockVerboseData *BlockVerboseData
|
||||||
|
|
||||||
Error *RPCError
|
Error *RPCError
|
||||||
}
|
}
|
||||||
@@ -39,3 +39,71 @@ func (msg *GetBlockResponseMessage) Command() MessageCommand {
|
|||||||
func NewGetBlockResponseMessage() *GetBlockResponseMessage {
|
func NewGetBlockResponseMessage() *GetBlockResponseMessage {
|
||||||
return &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
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ type GetBlockDAGInfoResponseMessage struct {
|
|||||||
Difficulty float64
|
Difficulty float64
|
||||||
PastMedianTime int64
|
PastMedianTime int64
|
||||||
PruningPointHash string
|
PruningPointHash string
|
||||||
VirtualDAAScore uint64
|
|
||||||
|
|
||||||
Error *RPCError
|
Error *RPCError
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ func NewGetBlockTemplateRequestMessage(payAddress string) *GetBlockTemplateReque
|
|||||||
// its respective RPC message
|
// its respective RPC message
|
||||||
type GetBlockTemplateResponseMessage struct {
|
type GetBlockTemplateResponseMessage struct {
|
||||||
baseMessage
|
baseMessage
|
||||||
Block *RPCBlock
|
MsgBlock *MsgBlock
|
||||||
IsSynced bool
|
IsSynced bool
|
||||||
|
|
||||||
Error *RPCError
|
Error *RPCError
|
||||||
@@ -35,9 +35,9 @@ func (msg *GetBlockTemplateResponseMessage) Command() MessageCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewGetBlockTemplateResponseMessage returns a instance of the message
|
// NewGetBlockTemplateResponseMessage returns a instance of the message
|
||||||
func NewGetBlockTemplateResponseMessage(block *RPCBlock, isSynced bool) *GetBlockTemplateResponseMessage {
|
func NewGetBlockTemplateResponseMessage(msgBlock *MsgBlock, isSynced bool) *GetBlockTemplateResponseMessage {
|
||||||
return &GetBlockTemplateResponseMessage{
|
return &GetBlockTemplateResponseMessage{
|
||||||
Block: block,
|
MsgBlock: msgBlock,
|
||||||
IsSynced: isSynced,
|
IsSynced: isSynced,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ package appmessage
|
|||||||
// its respective RPC message
|
// its respective RPC message
|
||||||
type GetBlocksRequestMessage struct {
|
type GetBlocksRequestMessage struct {
|
||||||
baseMessage
|
baseMessage
|
||||||
LowHash string
|
LowHash string
|
||||||
IncludeBlocks bool
|
IncludeBlockVerboseData bool
|
||||||
IncludeTransactions bool
|
IncludeTransactionVerboseData bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
// 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
|
// NewGetBlocksRequestMessage returns a instance of the message
|
||||||
func NewGetBlocksRequestMessage(lowHash string, includeBlocks bool,
|
func NewGetBlocksRequestMessage(lowHash string, includeBlockVerboseData bool,
|
||||||
includeTransactions bool) *GetBlocksRequestMessage {
|
includeTransactionVerboseData bool) *GetBlocksRequestMessage {
|
||||||
return &GetBlocksRequestMessage{
|
return &GetBlocksRequestMessage{
|
||||||
LowHash: lowHash,
|
LowHash: lowHash,
|
||||||
IncludeBlocks: includeBlocks,
|
IncludeBlockVerboseData: includeBlockVerboseData,
|
||||||
IncludeTransactions: includeTransactions,
|
IncludeTransactionVerboseData: includeTransactionVerboseData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,8 +28,8 @@ func NewGetBlocksRequestMessage(lowHash string, includeBlocks bool,
|
|||||||
// its respective RPC message
|
// its respective RPC message
|
||||||
type GetBlocksResponseMessage struct {
|
type GetBlocksResponseMessage struct {
|
||||||
baseMessage
|
baseMessage
|
||||||
BlockHashes []string
|
BlockHashes []string
|
||||||
Blocks []*RPCBlock
|
BlockVerboseData []*BlockVerboseData
|
||||||
|
|
||||||
Error *RPCError
|
Error *RPCError
|
||||||
}
|
}
|
||||||
@@ -40,6 +40,11 @@ func (msg *GetBlocksResponseMessage) Command() MessageCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewGetBlocksResponseMessage returns a instance of the message
|
// NewGetBlocksResponseMessage returns a instance of the message
|
||||||
func NewGetBlocksResponseMessage() *GetBlocksResponseMessage {
|
func NewGetBlocksResponseMessage(blockHashes []string, blockHexes []string,
|
||||||
return &GetBlocksResponseMessage{}
|
blockVerboseData []*BlockVerboseData) *GetBlocksResponseMessage {
|
||||||
|
|
||||||
|
return &GetBlocksResponseMessage{
|
||||||
|
BlockHashes: blockHashes,
|
||||||
|
BlockVerboseData: blockVerboseData,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ func (msg *GetInfoRequestMessage) Command() MessageCommand {
|
|||||||
return CmdGetInfoRequestMessage
|
return CmdGetInfoRequestMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGetInfoRequestMessage returns a instance of the message
|
// NewGeInfoRequestMessage returns a instance of the message
|
||||||
func NewGetInfoRequestMessage() *GetInfoRequestMessage {
|
func NewGeInfoRequestMessage() *GetInfoRequestMessage {
|
||||||
return &GetInfoRequestMessage{}
|
return &GetInfoRequestMessage{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,8 +20,7 @@ func NewGetInfoRequestMessage() *GetInfoRequestMessage {
|
|||||||
// its respective RPC message
|
// its respective RPC message
|
||||||
type GetInfoResponseMessage struct {
|
type GetInfoResponseMessage struct {
|
||||||
baseMessage
|
baseMessage
|
||||||
P2PID string
|
P2PID string
|
||||||
MempoolSize uint64
|
|
||||||
|
|
||||||
Error *RPCError
|
Error *RPCError
|
||||||
}
|
}
|
||||||
@@ -32,9 +31,8 @@ func (msg *GetInfoResponseMessage) Command() MessageCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewGetInfoResponseMessage returns a instance of the message
|
// NewGetInfoResponseMessage returns a instance of the message
|
||||||
func NewGetInfoResponseMessage(p2pID string, mempoolSize uint64) *GetInfoResponseMessage {
|
func NewGetInfoResponseMessage(p2pID string) *GetInfoResponseMessage {
|
||||||
return &GetInfoResponseMessage{
|
return &GetInfoResponseMessage{
|
||||||
P2PID: p2pID,
|
P2PID: p2pID,
|
||||||
MempoolSize: mempoolSize,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ type GetMempoolEntryResponseMessage struct {
|
|||||||
|
|
||||||
// MempoolEntry represents a transaction in the mempool.
|
// MempoolEntry represents a transaction in the mempool.
|
||||||
type MempoolEntry struct {
|
type MempoolEntry struct {
|
||||||
Fee uint64
|
Fee uint64
|
||||||
Transaction *RPCTransaction
|
TransactionVerboseData *TransactionVerboseData
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
// 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
|
// NewGetMempoolEntryResponseMessage returns a instance of the message
|
||||||
func NewGetMempoolEntryResponseMessage(fee uint64, transaction *RPCTransaction) *GetMempoolEntryResponseMessage {
|
func NewGetMempoolEntryResponseMessage(fee uint64, transactionVerboseData *TransactionVerboseData) *GetMempoolEntryResponseMessage {
|
||||||
return &GetMempoolEntryResponseMessage{
|
return &GetMempoolEntryResponseMessage{
|
||||||
Entry: &MempoolEntry{
|
Entry: &MempoolEntry{
|
||||||
Fee: fee,
|
Fee: fee,
|
||||||
Transaction: transaction,
|
TransactionVerboseData: transactionVerboseData,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ func NewNotifyBlockAddedResponseMessage() *NotifyBlockAddedResponseMessage {
|
|||||||
// its respective RPC message
|
// its respective RPC message
|
||||||
type BlockAddedNotificationMessage struct {
|
type BlockAddedNotificationMessage struct {
|
||||||
baseMessage
|
baseMessage
|
||||||
Block *RPCBlock
|
Block *MsgBlock
|
||||||
|
BlockVerboseData *BlockVerboseData
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
// 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
|
// NewBlockAddedNotificationMessage returns a instance of the message
|
||||||
func NewBlockAddedNotificationMessage(block *RPCBlock) *BlockAddedNotificationMessage {
|
func NewBlockAddedNotificationMessage(block *MsgBlock, blockVerboseData *BlockVerboseData) *BlockAddedNotificationMessage {
|
||||||
return &BlockAddedNotificationMessage{
|
return &BlockAddedNotificationMessage{
|
||||||
Block: block,
|
Block: block,
|
||||||
|
BlockVerboseData: blockVerboseData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@ package appmessage
|
|||||||
// its respective RPC message
|
// its respective RPC message
|
||||||
type SubmitBlockRequestMessage struct {
|
type SubmitBlockRequestMessage struct {
|
||||||
baseMessage
|
baseMessage
|
||||||
Block *RPCBlock
|
Block *MsgBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
// 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
|
// NewSubmitBlockRequestMessage returns a instance of the message
|
||||||
func NewSubmitBlockRequestMessage(block *RPCBlock) *SubmitBlockRequestMessage {
|
func NewSubmitBlockRequestMessage(block *MsgBlock) *SubmitBlockRequestMessage {
|
||||||
return &SubmitBlockRequestMessage{
|
return &SubmitBlockRequestMessage{
|
||||||
Block: block,
|
Block: block,
|
||||||
}
|
}
|
||||||
@@ -57,35 +57,3 @@ func (msg *SubmitBlockResponseMessage) Command() MessageCommand {
|
|||||||
func NewSubmitBlockResponseMessage() *SubmitBlockResponseMessage {
|
func NewSubmitBlockResponseMessage() *SubmitBlockResponseMessage {
|
||||||
return &SubmitBlockResponseMessage{}
|
return &SubmitBlockResponseMessage{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RPCBlock is a kaspad block representation meant to be
|
|
||||||
// used over RPC
|
|
||||||
type RPCBlock struct {
|
|
||||||
Header *RPCBlockHeader
|
|
||||||
Transactions []*RPCTransaction
|
|
||||||
VerboseData *RPCBlockVerboseData
|
|
||||||
}
|
|
||||||
|
|
||||||
// RPCBlockHeader is a kaspad block header representation meant to be
|
|
||||||
// used over RPC
|
|
||||||
type RPCBlockHeader struct {
|
|
||||||
Version uint32
|
|
||||||
ParentHashes []string
|
|
||||||
HashMerkleRoot string
|
|
||||||
AcceptedIDMerkleRoot string
|
|
||||||
UTXOCommitment string
|
|
||||||
Timestamp int64
|
|
||||||
Bits uint32
|
|
||||||
Nonce uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
// RPCBlockVerboseData holds verbose data about a block
|
|
||||||
type RPCBlockVerboseData struct {
|
|
||||||
Hash string
|
|
||||||
Difficulty float64
|
|
||||||
SelectedParentHash string
|
|
||||||
TransactionIDs []string
|
|
||||||
IsHeaderOnly bool
|
|
||||||
BlueScore uint64
|
|
||||||
ChildrenHashes []string
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ type RPCTransaction struct {
|
|||||||
LockTime uint64
|
LockTime uint64
|
||||||
SubnetworkID string
|
SubnetworkID string
|
||||||
Gas uint64
|
Gas uint64
|
||||||
|
PayloadHash string
|
||||||
Payload string
|
Payload string
|
||||||
VerboseData *RPCTransactionVerboseData
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RPCTransactionInput is a kaspad transaction input representation
|
// RPCTransactionInput is a kaspad transaction input representation
|
||||||
@@ -59,7 +59,6 @@ type RPCTransactionInput struct {
|
|||||||
PreviousOutpoint *RPCOutpoint
|
PreviousOutpoint *RPCOutpoint
|
||||||
SignatureScript string
|
SignatureScript string
|
||||||
Sequence uint64
|
Sequence uint64
|
||||||
VerboseData *RPCTransactionInputVerboseData
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RPCScriptPublicKey is a kaspad ScriptPublicKey representation
|
// RPCScriptPublicKey is a kaspad ScriptPublicKey representation
|
||||||
@@ -73,7 +72,6 @@ type RPCScriptPublicKey struct {
|
|||||||
type RPCTransactionOutput struct {
|
type RPCTransactionOutput struct {
|
||||||
Amount uint64
|
Amount uint64
|
||||||
ScriptPublicKey *RPCScriptPublicKey
|
ScriptPublicKey *RPCScriptPublicKey
|
||||||
VerboseData *RPCTransactionOutputVerboseData
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RPCOutpoint is a kaspad outpoint representation meant to be used
|
// RPCOutpoint is a kaspad outpoint representation meant to be used
|
||||||
@@ -88,25 +86,6 @@ type RPCOutpoint struct {
|
|||||||
type RPCUTXOEntry struct {
|
type RPCUTXOEntry struct {
|
||||||
Amount uint64
|
Amount uint64
|
||||||
ScriptPublicKey *RPCScriptPublicKey
|
ScriptPublicKey *RPCScriptPublicKey
|
||||||
BlockDAAScore uint64
|
BlockBlueScore uint64
|
||||||
IsCoinbase bool
|
IsCoinbase bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// RPCTransactionVerboseData holds verbose data about a transaction
|
|
||||||
type RPCTransactionVerboseData struct {
|
|
||||||
TransactionID string
|
|
||||||
Hash string
|
|
||||||
Size uint64
|
|
||||||
BlockHash string
|
|
||||||
BlockTime uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
// RPCTransactionInputVerboseData holds data about a transaction input
|
|
||||||
type RPCTransactionInputVerboseData struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// RPCTransactionOutputVerboseData holds data about a transaction output
|
|
||||||
type RPCTransactionOutputVerboseData struct {
|
|
||||||
ScriptPublicKeyType string
|
|
||||||
ScriptPublicKeyAddress string
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,17 +4,23 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"sync/atomic"
|
"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/protocol"
|
||||||
"github.com/kaspanet/kaspad/app/rpc"
|
"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"
|
"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/connmanager"
|
||||||
|
"github.com/kaspanet/kaspad/infrastructure/network/dnsseed"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter"
|
"github.com/kaspanet/kaspad/infrastructure/network/netadapter"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/id"
|
|
||||||
"github.com/kaspanet/kaspad/util/panics"
|
"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))
|
panics.Exit(log, fmt.Sprintf("Error starting the net adapter: %+v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.maybeSeedFromDNS()
|
||||||
|
|
||||||
a.connectionManager.Start()
|
a.connectionManager.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,8 +72,6 @@ func (a *ComponentManager) Stop() {
|
|||||||
log.Errorf("Error stopping the net adapter: %+v", err)
|
log.Errorf("Error stopping the net adapter: %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
a.protocolManager.Close()
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,13 +80,7 @@ func (a *ComponentManager) Stop() {
|
|||||||
func NewComponentManager(cfg *config.Config, db infrastructuredatabase.Database, interrupt chan<- struct{}) (
|
func NewComponentManager(cfg *config.Config, db infrastructuredatabase.Database, interrupt chan<- struct{}) (
|
||||||
*ComponentManager, error) {
|
*ComponentManager, error) {
|
||||||
|
|
||||||
consensusConfig := consensus.Config{
|
domain, err := domain.New(cfg.ActiveNetParams, db, cfg.IsArchivalNode)
|
||||||
Params: *cfg.ActiveNetParams,
|
|
||||||
IsArchival: cfg.IsArchivalNode,
|
|
||||||
EnableSanityCheckPruningUTXOSet: cfg.EnableSanityCheckPruningUTXOSet,
|
|
||||||
}
|
|
||||||
|
|
||||||
domain, err := domain.New(&consensusConfig, db)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -153,6 +153,23 @@ func setupRPC(
|
|||||||
return rpcManager
|
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
|
// P2PNodeID returns the network ID associated with this ComponentManager
|
||||||
func (a *ComponentManager) P2PNodeID() *id.ID {
|
func (a *ComponentManager) P2PNodeID() *id.ID {
|
||||||
return a.netAdapter.ID()
|
return a.netAdapter.ID()
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -37,14 +37,12 @@ func (f *FlowContext) OnNewBlock(block *externalapi.DomainBlock,
|
|||||||
newBlockInsertionResults = append(newBlockInsertionResults, unorphaningResult.blockInsertionResult)
|
newBlockInsertionResults = append(newBlockInsertionResults, unorphaningResult.blockInsertionResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
allAcceptedTransactions := make([]*externalapi.DomainTransaction, 0)
|
|
||||||
for i, newBlock := range newBlocks {
|
for i, newBlock := range newBlocks {
|
||||||
log.Debugf("OnNewBlock: passing block %s transactions to mining manager", hash)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
allAcceptedTransactions = append(allAcceptedTransactions, acceptedTransactions...)
|
|
||||||
|
|
||||||
if f.onBlockAddedToDAGHandler != nil {
|
if f.onBlockAddedToDAGHandler != nil {
|
||||||
log.Debugf("OnNewBlock: calling f.onBlockAddedToDAGHandler for block %s", hash)
|
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
|
// OnPruningPointUTXOSetOverride calls the handler function whenever the UTXO set
|
||||||
@@ -69,9 +67,9 @@ func (f *FlowContext) OnPruningPointUTXOSetOverride() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *FlowContext) broadcastTransactionsAfterBlockAdded(
|
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.
|
// Don't relay transactions when in IBD.
|
||||||
if f.IsIBDRunning() {
|
if f.IsIBDRunning() {
|
||||||
|
|||||||
@@ -61,8 +61,6 @@ type FlowContext struct {
|
|||||||
|
|
||||||
orphans map[externalapi.DomainHash]*externalapi.DomainBlock
|
orphans map[externalapi.DomainHash]*externalapi.DomainBlock
|
||||||
orphansMutex sync.RWMutex
|
orphansMutex sync.RWMutex
|
||||||
|
|
||||||
shutdownChan chan struct{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new instance of FlowContext.
|
// 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),
|
transactionsToRebroadcast: make(map[externalapi.DomainTransactionID]*externalapi.DomainTransaction),
|
||||||
orphans: make(map[externalapi.DomainHash]*externalapi.DomainBlock),
|
orphans: make(map[externalapi.DomainHash]*externalapi.DomainBlock),
|
||||||
timeStarted: mstime.Now().UnixMilliseconds(),
|
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
|
// SetOnBlockAddedToDAGHandler sets the onBlockAddedToDAG handler
|
||||||
func (f *FlowContext) SetOnBlockAddedToDAGHandler(onBlockAddedToDAGHandler OnBlockAddedToDAGHandler) {
|
func (f *FlowContext) SetOnBlockAddedToDAGHandler(onBlockAddedToDAGHandler OnBlockAddedToDAGHandler) {
|
||||||
f.onBlockAddedToDAGHandler = onBlockAddedToDAGHandler
|
f.onBlockAddedToDAGHandler = onBlockAddedToDAGHandler
|
||||||
|
|||||||
@@ -25,17 +25,14 @@ func (f *FlowContext) AddTransaction(tx *externalapi.DomainTransaction) error {
|
|||||||
return f.Broadcast(inv)
|
return f.Broadcast(inv)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FlowContext) updateTransactionsToRebroadcast(addedBlocks []*externalapi.DomainBlock) {
|
func (f *FlowContext) updateTransactionsToRebroadcast(block *externalapi.DomainBlock) {
|
||||||
f.transactionsToRebroadcastLock.Lock()
|
f.transactionsToRebroadcastLock.Lock()
|
||||||
defer f.transactionsToRebroadcastLock.Unlock()
|
defer f.transactionsToRebroadcastLock.Unlock()
|
||||||
|
// Note: if the block is red, its transactions won't be rebroadcasted
|
||||||
for _, block := range addedBlocks {
|
// anymore, although they are not included in the UTXO set.
|
||||||
// Note: if a transaction is included in the DAG but not accepted,
|
// This is probably ok, since red blocks are quite rare.
|
||||||
// it won't be rebroadcast anymore, although it is not included in
|
for _, tx := range block.Transactions {
|
||||||
// the UTXO set
|
delete(f.transactionsToRebroadcast, *consensushashing.TransactionID(tx))
|
||||||
for _, tx := range block.Transactions {
|
|
||||||
delete(f.transactionsToRebroadcast, *consensushashing.TransactionID(tx))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ func (flow *handleRequestHeadersFlow) start() error {
|
|||||||
// GetHashesBetween is a relatively heavy operation so we limit it
|
// GetHashesBetween is a relatively heavy operation so we limit it
|
||||||
// in order to avoid locking the consensus for too long
|
// in order to avoid locking the consensus for too long
|
||||||
const maxBlueScoreDifference = 1 << 10
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
|
|
||||||
// SendPingsContext is the interface for the context needed for the SendPings flow.
|
// SendPingsContext is the interface for the context needed for the SendPings flow.
|
||||||
type SendPingsContext interface {
|
type SendPingsContext interface {
|
||||||
ShutdownChan() <-chan struct{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type sendPingsFlow struct {
|
type sendPingsFlow struct {
|
||||||
@@ -40,13 +39,7 @@ func (flow *sendPingsFlow) start() error {
|
|||||||
ticker := time.NewTicker(pingInterval)
|
ticker := time.NewTicker(pingInterval)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
for {
|
for range ticker.C {
|
||||||
select {
|
|
||||||
case <-flow.ShutdownChan():
|
|
||||||
return nil
|
|
||||||
case <-ticker.C:
|
|
||||||
}
|
|
||||||
|
|
||||||
nonce, err := random.Uint64()
|
nonce, err := random.Uint64()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -69,4 +62,5 @@ func (flow *sendPingsFlow) start() error {
|
|||||||
}
|
}
|
||||||
flow.peer.SetPingIdle()
|
flow.peer.SetPingIdle()
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"github.com/kaspanet/kaspad/app/protocol/flows/blockrelay"
|
"github.com/kaspanet/kaspad/app/protocol/flows/blockrelay"
|
||||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||||
"github.com/kaspanet/kaspad/domain"
|
"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/model/externalapi"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
|
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
|
||||||
@@ -130,15 +129,7 @@ type fakeRelayInvsContext struct {
|
|||||||
rwLock sync.RWMutex
|
rwLock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeRelayInvsContext) EstimateNetworkHashesPerSecond(startHash *externalapi.DomainHash, windowSize int) (uint64, error) {
|
func (f *fakeRelayInvsContext) GetBlockChildren(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
|
||||||
panic(errors.Errorf("called unimplemented function from test '%s'", f.testName))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *fakeRelayInvsContext) GetBlockEvenIfHeaderOnly(blockHash *externalapi.DomainHash) (*externalapi.DomainBlock, error) {
|
|
||||||
panic("implement me")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *fakeRelayInvsContext) GetBlockRelations(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, *externalapi.DomainHash, []*externalapi.DomainHash, error) {
|
|
||||||
panic(errors.Errorf("called unimplemented function from test '%s'", f.testName))
|
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))
|
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))
|
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 {
|
for _, test := range tests {
|
||||||
|
|
||||||
// This is done to avoid race condition
|
// This is done to avoid race condition
|
||||||
@@ -1520,7 +1511,7 @@ func TestHandleRelayInvs(t *testing.T) {
|
|||||||
errChan := make(chan error)
|
errChan := make(chan error)
|
||||||
context := &fakeRelayInvsContext{
|
context := &fakeRelayInvsContext{
|
||||||
testName: test.name,
|
testName: test.name,
|
||||||
params: &consensusConfig.Params,
|
params: params,
|
||||||
finishedIBD: make(chan struct{}),
|
finishedIBD: make(chan struct{}),
|
||||||
|
|
||||||
trySetIBDRunningResponse: true,
|
trySetIBDRunningResponse: true,
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
package testing
|
package testing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/app/appmessage"
|
"github.com/kaspanet/kaspad/app/appmessage"
|
||||||
"github.com/kaspanet/kaspad/app/protocol/flows/addressexchange"
|
"github.com/kaspanet/kaspad/app/protocol/flows/addressexchange"
|
||||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
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/consensus/utils/testutils"
|
||||||
|
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
|
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type fakeReceiveAddressesContext struct{}
|
type fakeReceiveAddressesContext struct{}
|
||||||
@@ -20,7 +19,7 @@ func (f fakeReceiveAddressesContext) AddressManager() *addressmanager.AddressMan
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestReceiveAddressesErrors(t *testing.T) {
|
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()
|
incomingRoute := router.NewRoute()
|
||||||
outgoingRoute := router.NewRoute()
|
outgoingRoute := router.NewRoute()
|
||||||
peer := peerpkg.New(nil)
|
peer := peerpkg.New(nil)
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -2,9 +2,6 @@ package protocol
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/pkg/errors"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain"
|
"github.com/kaspanet/kaspad/domain"
|
||||||
|
|
||||||
@@ -20,9 +17,7 @@ import (
|
|||||||
|
|
||||||
// Manager manages the p2p protocol
|
// Manager manages the p2p protocol
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
context *flowcontext.FlowContext
|
context *flowcontext.FlowContext
|
||||||
routersWaitGroup sync.WaitGroup
|
|
||||||
isClosed uint32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManager creates a new instance of the p2p protocol manager
|
// 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
|
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
|
// Peers returns the currently active peers
|
||||||
func (m *Manager) Peers() []*peerpkg.Peer {
|
func (m *Manager) Peers() []*peerpkg.Peer {
|
||||||
return m.context.Peers()
|
return m.context.Peers()
|
||||||
@@ -70,13 +53,11 @@ func (m *Manager) AddBlock(block *externalapi.DomainBlock) error {
|
|||||||
return m.context.AddBlock(block)
|
return m.context.AddBlock(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) runFlows(flows []*flow, peer *peerpkg.Peer, errChan <-chan error, flowsWaitGroup *sync.WaitGroup) error {
|
func (m *Manager) runFlows(flows []*flow, peer *peerpkg.Peer, errChan <-chan error) error {
|
||||||
flowsWaitGroup.Add(len(flows))
|
|
||||||
for _, flow := range flows {
|
for _, flow := range flows {
|
||||||
executeFunc := flow.executeFunc // extract to new variable so that it's not overwritten
|
executeFunc := flow.executeFunc // extract to new variable so that it's not overwritten
|
||||||
spawn(fmt.Sprintf("flow-%s", flow.name), func() {
|
spawn(fmt.Sprintf("flow-%s", flow.name), func() {
|
||||||
executeFunc(peer)
|
executeFunc(peer)
|
||||||
flowsWaitGroup.Done()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
"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/appmessage"
|
||||||
"github.com/kaspanet/kaspad/app/protocol/flows/addressexchange"
|
"github.com/kaspanet/kaspad/app/protocol/flows/addressexchange"
|
||||||
"github.com/kaspanet/kaspad/app/protocol/flows/blockrelay"
|
"github.com/kaspanet/kaspad/app/protocol/flows/blockrelay"
|
||||||
"github.com/kaspanet/kaspad/app/protocol/flows/handshake"
|
"github.com/kaspanet/kaspad/app/protocol/flows/handshake"
|
||||||
"github.com/kaspanet/kaspad/app/protocol/flows/ping"
|
"github.com/kaspanet/kaspad/app/protocol/flows/ping"
|
||||||
"github.com/kaspanet/kaspad/app/protocol/flows/rejects"
|
|
||||||
"github.com/kaspanet/kaspad/app/protocol/flows/transactionrelay"
|
"github.com/kaspanet/kaspad/app/protocol/flows/transactionrelay"
|
||||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
|
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/network/connmanager"
|
|
||||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter"
|
"github.com/kaspanet/kaspad/infrastructure/network/netadapter"
|
||||||
routerpkg "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
routerpkg "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||||
"github.com/pkg/errors"
|
"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
|
// After flows were registered - spawn a new thread that will wait for connection to finish initializing
|
||||||
// and start receiving messages
|
// and start receiving messages
|
||||||
spawn("routerInitializer-runFlows", func() {
|
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)
|
isBanned, err := m.context.ConnectionManager().IsBanned(netConnection)
|
||||||
if err != nil && !errors.Is(err, addressmanager.ErrAddressNotFound) {
|
if err != nil && !errors.Is(err, addressmanager.ErrAddressNotFound) {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -86,17 +79,11 @@ func (m *Manager) routerInitializer(router *routerpkg.Router, netConnection *net
|
|||||||
|
|
||||||
removeHandshakeRoutes(router)
|
removeHandshakeRoutes(router)
|
||||||
|
|
||||||
flowsWaitGroup := &sync.WaitGroup{}
|
err = m.runFlows(flows, peer, errChan)
|
||||||
err = m.runFlows(flows, peer, errChan, flowsWaitGroup)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.handleError(err, netConnection, router.OutgoingRoute())
|
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
|
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)
|
log.Warnf("Banning %s (reason: %s)", netConnection, protocolErr.Cause)
|
||||||
|
|
||||||
err := m.context.ConnectionManager().Ban(netConnection)
|
err := m.context.ConnectionManager().Ban(netConnection)
|
||||||
if err != nil && !errors.Is(err, connmanager.ErrCannotBanPermanent) {
|
if !errors.Is(err, connmanager.ErrCannotBanPermanent) {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,7 +238,7 @@ func (m *Manager) registerTransactionRelayFlow(router *routerpkg.Router, isStopp
|
|||||||
outgoingRoute := router.OutgoingRoute()
|
outgoingRoute := router.OutgoingRoute()
|
||||||
|
|
||||||
return []*flow{
|
return []*flow{
|
||||||
m.registerFlowWithCapacity("HandleRelayedTransactions", 10_000, router,
|
m.registerFlow("HandleRelayedTransactions", router,
|
||||||
[]appmessage.MessageCommand{appmessage.CmdInvTransaction, appmessage.CmdTx, appmessage.CmdTransactionNotFound}, isStopping, errChan,
|
[]appmessage.MessageCommand{appmessage.CmdInvTransaction, appmessage.CmdTx, appmessage.CmdTransactionNotFound}, isStopping, errChan,
|
||||||
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
|
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
|
||||||
return transactionrelay.HandleRelayedTransactions(m.context, incomingRoute, outgoingRoute)
|
return transactionrelay.HandleRelayedTransactions(m.context, incomingRoute, outgoingRoute)
|
||||||
@@ -287,24 +274,6 @@ func (m *Manager) registerFlow(name string, router *routerpkg.Router, messageTyp
|
|||||||
panic(err)
|
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{
|
return &flow{
|
||||||
name: name,
|
name: name,
|
||||||
executeFunc: func(peer *peerpkg.Peer) {
|
executeFunc: func(peer *peerpkg.Peer) {
|
||||||
|
|||||||
@@ -64,22 +64,17 @@ func (m *Manager) NotifyBlockAddedToDAG(block *externalapi.DomainBlock, blockIns
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = m.notifyVirtualDaaScoreChanged()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = m.notifyVirtualSelectedParentChainChanged(blockInsertionResult)
|
err = m.notifyVirtualSelectedParentChainChanged(blockInsertionResult)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
rpcBlock := appmessage.DomainBlockToRPCBlock(block)
|
msgBlock := appmessage.DomainBlockToMsgBlock(block)
|
||||||
err = m.context.PopulateBlockWithVerboseData(rpcBlock, block.Header, block, false)
|
blockVerboseData, err := m.context.BuildBlockVerboseData(block.Header, block, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
blockAddedNotification := appmessage.NewBlockAddedNotificationMessage(rpcBlock)
|
blockAddedNotification := appmessage.NewBlockAddedNotificationMessage(msgBlock, blockVerboseData)
|
||||||
return m.context.NotificationManager.NotifyBlockAdded(blockAddedNotification)
|
return m.context.NotificationManager.NotifyBlockAdded(blockAddedNotification)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,19 +153,6 @@ func (m *Manager) notifyVirtualSelectedParentBlueScoreChanged() error {
|
|||||||
return m.context.NotificationManager.NotifyVirtualSelectedParentBlueScoreChanged(notification)
|
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 {
|
func (m *Manager) notifyVirtualSelectedParentChainChanged(blockInsertionResult *externalapi.BlockInsertionResult) error {
|
||||||
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyVirtualSelectedParentChainChanged")
|
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyVirtualSelectedParentChainChanged")
|
||||||
defer onEnd()
|
defer onEnd()
|
||||||
|
|||||||
@@ -44,8 +44,6 @@ var handlers = map[appmessage.MessageCommand]handler{
|
|||||||
appmessage.CmdGetInfoRequestMessage: rpchandlers.HandleGetInfo,
|
appmessage.CmdGetInfoRequestMessage: rpchandlers.HandleGetInfo,
|
||||||
appmessage.CmdNotifyPruningPointUTXOSetOverrideRequestMessage: rpchandlers.HandleNotifyPruningPointUTXOSetOverrideRequest,
|
appmessage.CmdNotifyPruningPointUTXOSetOverrideRequestMessage: rpchandlers.HandleNotifyPruningPointUTXOSetOverrideRequest,
|
||||||
appmessage.CmdStopNotifyingPruningPointUTXOSetOverrideRequestMessage: rpchandlers.HandleStopNotifyingPruningPointUTXOSetOverrideRequest,
|
appmessage.CmdStopNotifyingPruningPointUTXOSetOverrideRequestMessage: rpchandlers.HandleStopNotifyingPruningPointUTXOSetOverrideRequest,
|
||||||
appmessage.CmdEstimateNetworkHashesPerSecondRequestMessage: rpchandlers.HandleEstimateNetworkHashesPerSecond,
|
|
||||||
appmessage.CmdNotifyVirtualDaaScoreChangedRequestMessage: rpchandlers.HandleNotifyVirtualDaaScoreChanged,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) routerInitializer(router *router.Router, netConnection *netadapter.NetConnection) {
|
func (m *Manager) routerInitializer(router *router.Router, netConnection *netadapter.NetConnection) {
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ type NotificationListener struct {
|
|||||||
propagateFinalityConflictResolvedNotifications bool
|
propagateFinalityConflictResolvedNotifications bool
|
||||||
propagateUTXOsChangedNotifications bool
|
propagateUTXOsChangedNotifications bool
|
||||||
propagateVirtualSelectedParentBlueScoreChangedNotifications bool
|
propagateVirtualSelectedParentBlueScoreChangedNotifications bool
|
||||||
propagateVirtualDaaScoreChangedNotifications bool
|
|
||||||
propagatePruningPointUTXOSetOverrideNotifications bool
|
propagatePruningPointUTXOSetOverrideNotifications bool
|
||||||
|
|
||||||
propagateUTXOsChangedNotificationAddresses map[utxoindex.ScriptPublicKeyString]*UTXOsChangedNotificationAddress
|
propagateUTXOsChangedNotificationAddresses map[utxoindex.ScriptPublicKeyString]*UTXOsChangedNotificationAddress
|
||||||
@@ -182,25 +181,6 @@ func (nm *NotificationManager) NotifyVirtualSelectedParentBlueScoreChanged(
|
|||||||
return nil
|
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
|
// NotifyPruningPointUTXOSetOverride notifies the notification manager that the UTXO index
|
||||||
// reset due to pruning point change via IBD.
|
// reset due to pruning point change via IBD.
|
||||||
func (nm *NotificationManager) NotifyPruningPointUTXOSetOverride() error {
|
func (nm *NotificationManager) NotifyPruningPointUTXOSetOverride() error {
|
||||||
@@ -328,12 +308,6 @@ func (nl *NotificationListener) PropagateVirtualSelectedParentBlueScoreChangedNo
|
|||||||
nl.propagateVirtualSelectedParentBlueScoreChangedNotifications = true
|
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
|
// PropagatePruningPointUTXOSetOverrideNotifications instructs the listener to send pruning point UTXO set override notifications
|
||||||
// to the remote listener.
|
// to the remote listener.
|
||||||
func (nl *NotificationListener) PropagatePruningPointUTXOSetOverrideNotifications() {
|
func (nl *NotificationListener) PropagatePruningPointUTXOSetOverrideNotifications() {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func ConvertUTXOOutpointEntryPairsToUTXOsByAddressesEntries(address string, pair
|
|||||||
UTXOEntry: &appmessage.RPCUTXOEntry{
|
UTXOEntry: &appmessage.RPCUTXOEntry{
|
||||||
Amount: utxoEntry.Amount(),
|
Amount: utxoEntry.Amount(),
|
||||||
ScriptPublicKey: &appmessage.RPCScriptPublicKey{Script: hex.EncodeToString(utxoEntry.ScriptPublicKey().Script), Version: utxoEntry.ScriptPublicKey().Version},
|
ScriptPublicKey: &appmessage.RPCScriptPublicKey{Script: hex.EncodeToString(utxoEntry.ScriptPublicKey().Script), Version: utxoEntry.ScriptPublicKey().Version},
|
||||||
BlockDAAScore: utxoEntry.BlockDAAScore(),
|
BlockBlueScore: utxoEntry.BlockBlueScore(),
|
||||||
IsCoinbase: utxoEntry.IsCoinbase(),
|
IsCoinbase: utxoEntry.IsCoinbase(),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,10 +2,14 @@ package rpccontext
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"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/pkg/errors"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
"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/estimatedsize"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
|
"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/app/appmessage"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
"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.
|
// ErrBuildBlockVerboseDataInvalidBlock indicates that a block that was given to BuildBlockVerboseData is invalid.
|
||||||
var ErrBuildBlockVerboseDataInvalidBlock = errors.New("ErrBuildBlockVerboseDataInvalidBlock")
|
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
|
// 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.
|
// minimum difficulty using the passed bits field from the header of a block.
|
||||||
func (ctx *Context) GetDifficultyRatio(bits uint32, params *dagconfig.Params) float64 {
|
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
|
// 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
|
// work limit directly because the block difficulty is encoded in a block
|
||||||
// with the compact form which loses precision.
|
// with the compact form which loses precision.
|
||||||
target := difficultyPackage.CompactToBig(bits)
|
target := difficulty.CompactToBig(bits)
|
||||||
|
|
||||||
difficulty := new(big.Rat).SetFrac(params.PowMax, target)
|
difficulty := new(big.Rat).SetFrac(params.PowMax, target)
|
||||||
diff, _ := difficulty.Float64()
|
diff, _ := difficulty.Float64()
|
||||||
@@ -40,128 +119,106 @@ func (ctx *Context) GetDifficultyRatio(bits uint32, params *dagconfig.Params) fl
|
|||||||
return diff
|
return diff
|
||||||
}
|
}
|
||||||
|
|
||||||
// PopulateBlockWithVerboseData populates the given `block` with verbose
|
// BuildTransactionVerboseData builds a TransactionVerboseData from
|
||||||
// data from `domainBlockHeader` and optionally from `domainBlock`
|
// the given parameters
|
||||||
func (ctx *Context) PopulateBlockWithVerboseData(block *appmessage.RPCBlock, domainBlockHeader externalapi.BlockHeader,
|
func (ctx *Context) BuildTransactionVerboseData(tx *externalapi.DomainTransaction, txID string,
|
||||||
domainBlock *externalapi.DomainBlock, includeTransactionVerboseData bool) error {
|
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)
|
var payloadHash string
|
||||||
if err != nil {
|
if tx.SubnetworkID != subnetworks.SubnetworkIDNative {
|
||||||
return err
|
payloadHash = tx.PayloadHash.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
if blockInfo.BlockStatus == externalapi.StatusInvalid {
|
txReply := &appmessage.TransactionVerboseData{
|
||||||
return errors.Wrap(ErrBuildBlockVerboseDataInvalidBlock, "cannot build verbose data for "+
|
TxID: txID,
|
||||||
"invalid block")
|
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 blockHeader != nil {
|
||||||
if err != nil {
|
txReply.Time = uint64(blockHeader.TimeInMilliseconds())
|
||||||
return err
|
txReply.BlockTime = uint64(blockHeader.TimeInMilliseconds())
|
||||||
|
txReply.BlockHash = blockHash
|
||||||
}
|
}
|
||||||
|
|
||||||
block.VerboseData = &appmessage.RPCBlockVerboseData{
|
return txReply, nil
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
if blockInfo.BlockStatus == externalapi.StatusHeaderOnly {
|
func (ctx *Context) buildTransactionVerboseInputs(tx *externalapi.DomainTransaction) []*appmessage.TransactionVerboseInput {
|
||||||
return nil
|
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
|
input := &appmessage.TransactionVerboseInput{}
|
||||||
if domainBlock == nil {
|
input.TxID = transactionInput.PreviousOutpoint.TransactionID.String()
|
||||||
domainBlock, err = ctx.Domain.Consensus().GetBlockEvenIfHeaderOnly(blockHash)
|
input.OutputIndex = transactionInput.PreviousOutpoint.Index
|
||||||
if err != nil {
|
input.Sequence = transactionInput.Sequence
|
||||||
return err
|
input.ScriptSig = &appmessage.ScriptSig{
|
||||||
|
Asm: disbuf,
|
||||||
|
Hex: hex.EncodeToString(transactionInput.SignatureScript),
|
||||||
}
|
}
|
||||||
|
inputs[i] = input
|
||||||
}
|
}
|
||||||
|
|
||||||
transactionIDs := make([]string, len(domainBlock.Transactions))
|
return inputs
|
||||||
for i, transaction := range domainBlock.Transactions {
|
}
|
||||||
transactionIDs[i] = consensushashing.TransactionID(transaction).String()
|
|
||||||
}
|
|
||||||
block.VerboseData.TransactionIDs = transactionIDs
|
|
||||||
|
|
||||||
if includeTransactionVerboseData {
|
// buildTransactionVerboseOutputs returns a slice of JSON objects for the outputs of the passed
|
||||||
for _, transaction := range block.Transactions {
|
// transaction.
|
||||||
err := ctx.PopulateTransactionWithVerboseData(transaction, domainBlockHeader)
|
func (ctx *Context) buildTransactionVerboseOutputs(tx *externalapi.DomainTransaction, filterAddrMap map[string]struct{}) []*appmessage.TransactionVerboseOutput {
|
||||||
if err != nil {
|
outputs := make([]*appmessage.TransactionVerboseOutput, len(tx.Outputs))
|
||||||
return err
|
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
|
if !passesFilter {
|
||||||
}
|
continue
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
|
||||||
}
|
return outputs
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -20,7 +20,7 @@ func HandleGetBlock(context *rpccontext.Context, _ *router.Router, request appme
|
|||||||
return errorMessage, nil
|
return errorMessage, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
block, err := context.Domain.Consensus().GetBlockEvenIfHeaderOnly(hash)
|
header, err := context.Domain.Consensus().GetBlockHeader(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorMessage := &appmessage.GetBlockResponseMessage{}
|
errorMessage := &appmessage.GetBlockResponseMessage{}
|
||||||
errorMessage.Error = appmessage.RPCErrorf("Block %s not found", hash)
|
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()
|
response := appmessage.NewGetBlockResponseMessage()
|
||||||
|
|
||||||
if getBlockRequest.IncludeTransactions {
|
blockVerboseData, err := context.BuildBlockVerboseData(header, nil, getBlockRequest.IncludeTransactionVerboseData)
|
||||||
response.Block = appmessage.DomainBlockToRPCBlock(block)
|
|
||||||
} else {
|
|
||||||
response.Block = appmessage.DomainBlockToRPCBlock(&externalapi.DomainBlock{Header: block.Header})
|
|
||||||
}
|
|
||||||
|
|
||||||
err = context.PopulateBlockWithVerboseData(response.Block, block.Header, block, getBlockRequest.IncludeTransactions)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, rpccontext.ErrBuildBlockVerboseDataInvalidBlock) {
|
if errors.Is(err, rpccontext.ErrBuildBlockVerboseDataInvalidBlock) {
|
||||||
errorMessage := &appmessage.GetBlockResponseMessage{}
|
errorMessage := &appmessage.GetBlockResponseMessage{}
|
||||||
@@ -45,5 +39,7 @@ func HandleGetBlock(context *rpccontext.Context, _ *router.Router, request appme
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
response.BlockVerboseData = blockVerboseData
|
||||||
|
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ func HandleGetBlockDAGInfo(context *rpccontext.Context, _ *router.Router, _ appm
|
|||||||
response.VirtualParentHashes = hashes.ToStrings(virtualInfo.ParentHashes)
|
response.VirtualParentHashes = hashes.ToStrings(virtualInfo.ParentHashes)
|
||||||
response.Difficulty = context.GetDifficultyRatio(virtualInfo.Bits, context.Config.ActiveNetParams)
|
response.Difficulty = context.GetDifficultyRatio(virtualInfo.Bits, context.Config.ActiveNetParams)
|
||||||
response.PastMedianTime = virtualInfo.PastMedianTime
|
response.PastMedianTime = virtualInfo.PastMedianTime
|
||||||
response.VirtualDAAScore = virtualInfo.DAAScore
|
|
||||||
|
|
||||||
pruningPoint, err := context.Domain.Consensus().PruningPoint()
|
pruningPoint, err := context.Domain.Consensus().PruningPoint()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -31,12 +31,12 @@ func HandleGetBlockTemplate(context *rpccontext.Context, _ *router.Router, reque
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rpcBlock := appmessage.DomainBlockToRPCBlock(templateBlock)
|
msgBlock := appmessage.DomainBlockToMsgBlock(templateBlock)
|
||||||
|
|
||||||
isSynced, err := context.ProtocolManager.ShouldMine()
|
isSynced, err := context.ProtocolManager.ShouldMine()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return appmessage.NewGetBlockTemplateResponseMessage(rpcBlock, isSynced), nil
|
return appmessage.NewGetBlockTemplateResponseMessage(msgBlock, isSynced), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ const (
|
|||||||
func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||||
getBlocksRequest := request.(*appmessage.GetBlocksRequestMessage)
|
getBlocksRequest := request.(*appmessage.GetBlocksRequestMessage)
|
||||||
|
|
||||||
// Validate that user didn't set IncludeTransactions without setting IncludeBlocks
|
// Validate that user didn't set IncludeTransactionVerboseData without setting IncludeBlockVerboseData
|
||||||
if !getBlocksRequest.IncludeBlocks && getBlocksRequest.IncludeTransactions {
|
if !getBlocksRequest.IncludeBlockVerboseData && getBlocksRequest.IncludeTransactionVerboseData {
|
||||||
return &appmessage.GetBlocksResponseMessage{
|
return &appmessage.GetBlocksResponseMessage{
|
||||||
Error: appmessage.RPCErrorf(
|
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
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +55,8 @@ func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appm
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -63,10 +64,9 @@ func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appm
|
|||||||
// prepend low hash to make it inclusive
|
// prepend low hash to make it inclusive
|
||||||
blockHashes = append([]*externalapi.DomainHash{lowHash}, blockHashes...)
|
blockHashes = append([]*externalapi.DomainHash{lowHash}, blockHashes...)
|
||||||
|
|
||||||
// If the high hash is equal to virtualSelectedParent it means GetHashesBetween didn't skip any hashes, and
|
// If there are no maxBlocksInGetBlocksResponse between lowHash and virtualSelectedParent -
|
||||||
// there's space to add the virtualSelectedParent's anticone, otherwise you can't add the anticone because
|
// add virtualSelectedParent's anticone
|
||||||
// there's no guarantee that all of the anticone root ancestors will be present.
|
if len(blockHashes) < maxBlocksInGetBlocksResponse {
|
||||||
if highHash.Equal(virtualSelectedParent) {
|
|
||||||
virtualSelectedParentAnticone, err := context.Domain.Consensus().Anticone(virtualSelectedParent)
|
virtualSelectedParentAnticone, err := context.Domain.Consensus().Anticone(virtualSelectedParent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -81,28 +81,26 @@ func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appm
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the response
|
// Prepare the response
|
||||||
response := appmessage.NewGetBlocksResponseMessage()
|
response := &appmessage.GetBlocksResponseMessage{
|
||||||
response.BlockHashes = hashes.ToStrings(blockHashes)
|
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/app/appmessage"
|
"github.com/kaspanet/kaspad/app/appmessage"
|
||||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||||
"github.com/kaspanet/kaspad/app/rpc/rpchandlers"
|
"github.com/kaspanet/kaspad/app/rpc/rpchandlers"
|
||||||
@@ -15,6 +13,7 @@ import (
|
|||||||
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
|
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||||
|
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||||
"github.com/kaspanet/kaspad/domain/miningmanager"
|
"github.com/kaspanet/kaspad/domain/miningmanager"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||||
)
|
)
|
||||||
@@ -27,18 +26,16 @@ func (d fakeDomain) Consensus() externalapi.Consensus { return d }
|
|||||||
func (d fakeDomain) MiningManager() miningmanager.MiningManager { return nil }
|
func (d fakeDomain) MiningManager() miningmanager.MiningManager { return nil }
|
||||||
|
|
||||||
func TestHandleGetBlocks(t *testing.T) {
|
func TestHandleGetBlocks(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) {
|
||||||
stagingArea := model.NewStagingArea()
|
|
||||||
|
|
||||||
factory := consensus.NewFactory()
|
factory := consensus.NewFactory()
|
||||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestHandleGetBlocks")
|
tc, teardown, err := factory.NewTestConsensus(params, false, "TestHandleGetBlocks")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error setting up consensus: %+v", err)
|
t.Fatalf("Error setting up consensus: %+v", err)
|
||||||
}
|
}
|
||||||
defer teardown(false)
|
defer teardown(false)
|
||||||
|
|
||||||
fakeContext := rpccontext.Context{
|
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},
|
Domain: fakeDomain{tc},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +55,7 @@ func TestHandleGetBlocks(t *testing.T) {
|
|||||||
antipast := make([]*externalapi.DomainHash, 0, len(slice))
|
antipast := make([]*externalapi.DomainHash, 0, len(slice))
|
||||||
|
|
||||||
for _, blockHash := range slice {
|
for _, blockHash := range slice {
|
||||||
isInPastOfPovBlock, err := tc.DAGTopologyManager().IsAncestorOf(stagingArea, blockHash, povBlock)
|
isInPastOfPovBlock, err := tc.DAGTopologyManager().IsAncestorOf(blockHash, povBlock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed doing reachability check: '%v'", err)
|
t.Fatalf("Failed doing reachability check: '%v'", err)
|
||||||
}
|
}
|
||||||
@@ -80,7 +77,7 @@ func TestHandleGetBlocks(t *testing.T) {
|
|||||||
// \ | /
|
// \ | /
|
||||||
// etc.
|
// etc.
|
||||||
expectedOrder := make([]*externalapi.DomainHash, 0, 40)
|
expectedOrder := make([]*externalapi.DomainHash, 0, 40)
|
||||||
mergingBlock := consensusConfig.GenesisHash
|
mergingBlock := params.GenesisHash
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
splitBlocks := make([]*externalapi.DomainHash, 0, 3)
|
splitBlocks := make([]*externalapi.DomainHash, 0, 3)
|
||||||
for j := 0; j < 3; j++ {
|
for j := 0; j < 3; j++ {
|
||||||
@@ -90,7 +87,7 @@ func TestHandleGetBlocks(t *testing.T) {
|
|||||||
}
|
}
|
||||||
splitBlocks = append(splitBlocks, blockHash)
|
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]
|
restOfSplitBlocks, selectedParent := splitBlocks[:len(splitBlocks)-1], splitBlocks[len(splitBlocks)-1]
|
||||||
expectedOrder = append(expectedOrder, selectedParent)
|
expectedOrder = append(expectedOrder, selectedParent)
|
||||||
expectedOrder = append(expectedOrder, restOfSplitBlocks...)
|
expectedOrder = append(expectedOrder, restOfSplitBlocks...)
|
||||||
@@ -133,13 +130,13 @@ func TestHandleGetBlocks(t *testing.T) {
|
|||||||
virtualSelectedParent, actualBlocks.BlockHashes)
|
virtualSelectedParent, actualBlocks.BlockHashes)
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedOrder = append([]*externalapi.DomainHash{consensusConfig.GenesisHash}, expectedOrder...)
|
expectedOrder = append([]*externalapi.DomainHash{params.GenesisHash}, expectedOrder...)
|
||||||
actualOrder := getBlocks(nil)
|
actualOrder := getBlocks(nil)
|
||||||
if !reflect.DeepEqual(actualOrder.BlockHashes, hashes.ToStrings(expectedOrder)) {
|
if !reflect.DeepEqual(actualOrder.BlockHashes, hashes.ToStrings(expectedOrder)) {
|
||||||
t.Fatalf("TestHandleGetBlocks \nexpected: %v \nactual:\n%v", expectedOrder, actualOrder.BlockHashes)
|
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)) {
|
if !reflect.DeepEqual(requestAllExplictly.BlockHashes, hashes.ToStrings(expectedOrder)) {
|
||||||
t.Fatalf("TestHandleGetBlocks \nexpected: \n%v\n. actual:\n%v", expectedOrder, requestAllExplictly.BlockHashes)
|
t.Fatalf("TestHandleGetBlocks \nexpected: \n%v\n. actual:\n%v", expectedOrder, requestAllExplictly.BlockHashes)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,6 @@ import (
|
|||||||
|
|
||||||
// HandleGetInfo handles the respectively named RPC command
|
// HandleGetInfo handles the respectively named RPC command
|
||||||
func HandleGetInfo(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
|
func HandleGetInfo(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
|
||||||
response := appmessage.NewGetInfoResponseMessage(
|
response := appmessage.NewGetInfoResponseMessage(context.NetAdapter.ID().String())
|
||||||
context.NetAdapter.ID().String(),
|
|
||||||
uint64(context.Domain.MiningManager().TransactionCount()),
|
|
||||||
)
|
|
||||||
|
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,22 +3,25 @@ package rpchandlers
|
|||||||
import (
|
import (
|
||||||
"github.com/kaspanet/kaspad/app/appmessage"
|
"github.com/kaspanet/kaspad/app/appmessage"
|
||||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HandleGetMempoolEntries handles the respectively named RPC command
|
// HandleGetMempoolEntries handles the respectively named RPC command
|
||||||
func HandleGetMempoolEntries(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
|
func HandleGetMempoolEntries(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
|
||||||
|
|
||||||
transactions := context.Domain.MiningManager().AllTransactions()
|
transactions := context.Domain.MiningManager().AllTransactions()
|
||||||
entries := make([]*appmessage.MempoolEntry, 0, len(transactions))
|
entries := make([]*appmessage.MempoolEntry, 0, len(transactions))
|
||||||
for _, transaction := range transactions {
|
for _, tx := range transactions {
|
||||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
transactionVerboseData, err := context.BuildTransactionVerboseData(
|
||||||
err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
|
tx, consensushashing.TransactionID(tx).String(), nil, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
entries = append(entries, &appmessage.MempoolEntry{
|
entries = append(entries, &appmessage.MempoolEntry{
|
||||||
Fee: transaction.Fee,
|
Fee: tx.Fee,
|
||||||
Transaction: rpcTransaction,
|
TransactionVerboseData: transactionVerboseData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,11 +24,12 @@ func HandleGetMempoolEntry(context *rpccontext.Context, _ *router.Router, reques
|
|||||||
errorMessage.Error = appmessage.RPCErrorf("Transaction %s was not found", transactionID)
|
errorMessage.Error = appmessage.RPCErrorf("Transaction %s was not found", transactionID)
|
||||||
return errorMessage, nil
|
return errorMessage, nil
|
||||||
}
|
}
|
||||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
|
||||||
err = context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
|
transactionVerboseData, err := context.BuildTransactionVerboseData(
|
||||||
|
transaction, getMempoolEntryRequest.TxID, nil, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return appmessage.NewGetMempoolEntryResponseMessage(transaction.Fee, rpcTransaction), nil
|
return appmessage.NewGetMempoolEntryResponseMessage(transaction.Fee, transactionVerboseData), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -14,6 +14,9 @@ import (
|
|||||||
func HandleSubmitBlock(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
func HandleSubmitBlock(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||||
submitBlockRequest := request.(*appmessage.SubmitBlockRequestMessage)
|
submitBlockRequest := request.(*appmessage.SubmitBlockRequestMessage)
|
||||||
|
|
||||||
|
msgBlock := submitBlockRequest.Block
|
||||||
|
domainBlock := appmessage.MsgBlockToDomainBlock(msgBlock)
|
||||||
|
|
||||||
if context.ProtocolManager.IsIBDRunning() {
|
if context.ProtocolManager.IsIBDRunning() {
|
||||||
return &appmessage.SubmitBlockResponseMessage{
|
return &appmessage.SubmitBlockResponseMessage{
|
||||||
Error: appmessage.RPCErrorf("Block not submitted - IBD is running"),
|
Error: appmessage.RPCErrorf("Block not submitted - IBD is running"),
|
||||||
@@ -21,15 +24,7 @@ func HandleSubmitBlock(context *rpccontext.Context, _ *router.Router, request ap
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
domainBlock, err := appmessage.RPCBlockToDomainBlock(submitBlockRequest.Block)
|
err := context.ProtocolManager.AddBlock(domainBlock)
|
||||||
if err != nil {
|
|
||||||
return &appmessage.SubmitBlockResponseMessage{
|
|
||||||
Error: appmessage.RPCErrorf("Could not parse block: %s", err),
|
|
||||||
RejectReason: appmessage.RejectReasonBlockInvalid,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err = context.ProtocolManager.AddBlock(domainBlock)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
isProtocolOrRuleError := errors.As(err, &ruleerrors.RuleError{}) || errors.As(err, &protocolerrors.ProtocolError{})
|
isProtocolOrRuleError := errors.As(err, &ruleerrors.RuleError{}) || errors.As(err, &protocolerrors.ProtocolError{})
|
||||||
if !isProtocolOrRuleError {
|
if !isProtocolOrRuleError {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func HandleUnban(context *rpccontext.Context, _ *router.Router, request appmessa
|
|||||||
errorMessage.Error = appmessage.RPCErrorf("Could not parse IP %s", unbanRequest.IP)
|
errorMessage.Error = appmessage.RPCErrorf("Could not parse IP %s", unbanRequest.IP)
|
||||||
return errorMessage, nil
|
return errorMessage, nil
|
||||||
}
|
}
|
||||||
err := context.AddressManager.Unban(appmessage.NewNetAddressIPPort(ip, 0))
|
err := context.AddressManager.Unban(appmessage.NewNetAddressIPPort(ip, 0, 0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorMessage := &appmessage.UnbanResponseMessage{}
|
errorMessage := &appmessage.UnbanResponseMessage{}
|
||||||
errorMessage.Error = appmessage.RPCErrorf("Could not unban IP: %s", err)
|
errorMessage.Error = appmessage.RPCErrorf("Could not unban IP: %s", err)
|
||||||
|
|||||||
@@ -1,63 +1,3 @@
|
|||||||
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
|
Kaspad v0.9.1 - 2021-03-14
|
||||||
===========================
|
===========================
|
||||||
|
|||||||
@@ -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)
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
@@ -48,10 +48,6 @@ func setField(commandValue reflect.Value, parameterValue reflect.Value, paramete
|
|||||||
}
|
}
|
||||||
|
|
||||||
func stringToValue(parameterDesc *parameterDescription, valueStr string) (reflect.Value, error) {
|
func stringToValue(parameterDesc *parameterDescription, valueStr string) (reflect.Value, error) {
|
||||||
if valueStr == "-" {
|
|
||||||
return reflect.Zero(parameterDesc.typeof), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var value interface{}
|
var value interface{}
|
||||||
var err error
|
var err error
|
||||||
switch parameterDesc.typeof.Kind() {
|
switch parameterDesc.typeof.Kind() {
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ var commandTypes = []reflect.Type{
|
|||||||
reflect.TypeOf(protowire.KaspadMessage_GetVirtualSelectedParentBlueScoreRequest{}),
|
reflect.TypeOf(protowire.KaspadMessage_GetVirtualSelectedParentBlueScoreRequest{}),
|
||||||
reflect.TypeOf(protowire.KaspadMessage_GetVirtualSelectedParentChainFromBlockRequest{}),
|
reflect.TypeOf(protowire.KaspadMessage_GetVirtualSelectedParentChainFromBlockRequest{}),
|
||||||
reflect.TypeOf(protowire.KaspadMessage_ResolveFinalityConflictRequest{}),
|
reflect.TypeOf(protowire.KaspadMessage_ResolveFinalityConflictRequest{}),
|
||||||
reflect.TypeOf(protowire.KaspadMessage_EstimateNetworkHashesPerSecondRequest{}),
|
|
||||||
|
|
||||||
reflect.TypeOf(protowire.KaspadMessage_GetBlockTemplateRequest{}),
|
reflect.TypeOf(protowire.KaspadMessage_GetBlockTemplateRequest{}),
|
||||||
reflect.TypeOf(protowire.KaspadMessage_SubmitBlockRequest{}),
|
reflect.TypeOf(protowire.KaspadMessage_SubmitBlockRequest{}),
|
||||||
|
|||||||
@@ -27,8 +27,7 @@ func parseConfig() (*configFlags, error) {
|
|||||||
}
|
}
|
||||||
parser := flags.NewParser(cfg, flags.HelpFlag)
|
parser := flags.NewParser(cfg, flags.HelpFlag)
|
||||||
parser.Usage = "kaspactl [OPTIONS] [COMMAND] [COMMAND PARAMETERS].\n\nCommand can be supplied only if --json is not used." +
|
parser.Usage = "kaspactl [OPTIONS] [COMMAND] [COMMAND PARAMETERS].\n\nCommand can be supplied only if --json is not used." +
|
||||||
"\n\nUse `kaspactl --list-commands` to get a list of all commands and their parameters." +
|
"\n\nUse `kaspactl --list-commands` to get a list of all commands and their parameters"
|
||||||
"\nFor optional parameters- use '-' without quotes to not pass the parameter.\n"
|
|
||||||
remainingArgs, err := parser.Parse()
|
remainingArgs, err := parser.Parse()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -5,32 +5,72 @@ import (
|
|||||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/network/rpcclient"
|
"github.com/kaspanet/kaspad/infrastructure/network/rpcclient"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const minerTimeout = 10 * time.Second
|
const minerTimeout = 10 * time.Second
|
||||||
|
|
||||||
type minerClient struct {
|
type minerClient struct {
|
||||||
*rpcclient.RPCClient
|
isReconnecting uint32
|
||||||
|
clientLock sync.RWMutex
|
||||||
|
rpcClient *rpcclient.RPCClient
|
||||||
|
|
||||||
cfg *configFlags
|
cfg *configFlags
|
||||||
blockAddedNotificationChan chan struct{}
|
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 {
|
func (mc *minerClient) connect() error {
|
||||||
rpcAddress, err := mc.cfg.NetParams().NormalizeRPCServerAddress(mc.cfg.RPCServer)
|
rpcAddress, err := mc.cfg.NetParams().NormalizeRPCServerAddress(mc.cfg.RPCServer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rpcClient, err := rpcclient.NewRPCClient(rpcAddress)
|
mc.rpcClient, err = rpcclient.NewRPCClient(rpcAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
mc.RPCClient = rpcClient
|
mc.rpcClient.SetTimeout(minerTimeout)
|
||||||
mc.SetTimeout(minerTimeout)
|
mc.rpcClient.SetLogger(backendLog, logger.LevelTrace)
|
||||||
mc.SetLogger(backendLog, logger.LevelTrace)
|
|
||||||
|
|
||||||
err = mc.RegisterForBlockAddedNotifications(func(_ *appmessage.BlockAddedNotificationMessage) {
|
err = mc.rpcClient.RegisterForBlockAddedNotifications(func(_ *appmessage.BlockAddedNotificationMessage) {
|
||||||
select {
|
select {
|
||||||
case mc.blockAddedNotificationChan <- struct{}{}:
|
case mc.blockAddedNotificationChan <- struct{}{}:
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ const (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
// Default configuration options
|
// Default configuration options
|
||||||
defaultAppDir = util.AppDir("kaspaminer", false)
|
defaultHomeDir = util.AppDataDir("kaspaminer", false)
|
||||||
defaultLogFile = filepath.Join(defaultAppDir, defaultLogFilename)
|
defaultLogFile = filepath.Join(defaultHomeDir, defaultLogFilename)
|
||||||
defaultErrLogFile = filepath.Join(defaultAppDir, defaultErrLogFilename)
|
defaultErrLogFile = filepath.Join(defaultHomeDir, defaultErrLogFilename)
|
||||||
defaultRPCServer = "localhost"
|
defaultRPCServer = "localhost"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(errors.Wrap(err, "error connecting to the RPC server"))
|
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)
|
miningAddr, err := util.DecodeAddress(cfg.MiningAddr, cfg.ActiveNetParams.Prefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -6,14 +6,18 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/app/appmessage"
|
|
||||||
"github.com/kaspanet/kaspad/cmd/kaspaminer/templatemanager"
|
"github.com/kaspanet/kaspad/cmd/kaspaminer/templatemanager"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/domain/consensus/model/pow"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/pow"
|
|
||||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
|
||||||
"github.com/kaspanet/kaspad/util"
|
|
||||||
"github.com/kaspanet/kaspad/util/difficulty"
|
"github.com/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"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -40,32 +44,34 @@ func mineLoop(client *minerClient, numberOfBlocks uint64, targetBlocksPerSecond
|
|||||||
|
|
||||||
spawn("blocksLoop", func() {
|
spawn("blocksLoop", func() {
|
||||||
const windowSize = 10
|
const windowSize = 10
|
||||||
|
var expectedDurationForWindow time.Duration
|
||||||
|
var windowExpectedEndTime time.Time
|
||||||
hasBlockRateTarget := targetBlocksPerSecond != 0
|
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 {
|
if hasBlockRateTarget {
|
||||||
windowRate := time.Duration(float64(time.Second) / (targetBlocksPerSecond / windowSize))
|
expectedDurationForWindow = time.Duration(float64(windowSize)/targetBlocksPerSecond) * time.Second
|
||||||
blockRate := time.Duration(float64(time.Second) / (targetBlocksPerSecond * windowSize))
|
windowExpectedEndTime = time.Now().Add(expectedDurationForWindow)
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
windowStart := time.Now()
|
blockInWindowIndex := 0
|
||||||
for blockIndex := 1; ; blockIndex++ {
|
|
||||||
|
sleepTime := 0 * time.Second
|
||||||
|
|
||||||
|
for {
|
||||||
foundBlockChan <- mineNextBlock(mineWhenNotSynced)
|
foundBlockChan <- mineNextBlock(mineWhenNotSynced)
|
||||||
|
|
||||||
if hasBlockRateTarget {
|
if hasBlockRateTarget {
|
||||||
<-blockTicker.C
|
blockInWindowIndex++
|
||||||
if (blockIndex % windowSize) == 0 {
|
if blockInWindowIndex == windowSize-1 {
|
||||||
tickerStart := time.Now()
|
deviation := windowExpectedEndTime.Sub(time.Now())
|
||||||
<-windowTicker.C
|
if deviation > 0 {
|
||||||
log.Infof("Finished mining %d blocks in: %s. slept for: %s", windowSize, time.Since(windowStart), time.Since(tickerStart))
|
sleepTime = deviation / windowSize
|
||||||
windowStart = time.Now()
|
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 {
|
func handleFoundBlock(client *minerClient, block *externalapi.DomainBlock) error {
|
||||||
blockHash := consensushashing.BlockHash(block)
|
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 err != nil {
|
||||||
if nativeerrors.Is(err, router.ErrTimeout) {
|
if nativeerrors.Is(err, router.ErrTimeout) {
|
||||||
log.Warnf("Got timeout while submitting block %s to %s: %s", blockHash, client.Address(), err)
|
log.Warnf("Got timeout while submitting block %s to %s: %s", blockHash, client.safeRPCClient().Address(), err)
|
||||||
return client.Reconnect()
|
client.reconnect()
|
||||||
}
|
|
||||||
if nativeerrors.Is(err, router.ErrRouteClosed) {
|
|
||||||
log.Debugf("Got route is closed while requesting block template from %s. "+
|
|
||||||
"The client is most likely reconnecting", client.Address())
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if rejectReason == appmessage.RejectReasonIsInIBD {
|
if rejectReason == appmessage.RejectReasonIsInIBD {
|
||||||
@@ -129,7 +131,7 @@ func handleFoundBlock(client *minerClient, block *externalapi.DomainBlock) error
|
|||||||
time.Sleep(waitTime)
|
time.Sleep(waitTime)
|
||||||
return nil
|
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
|
return nil
|
||||||
}
|
}
|
||||||
@@ -188,29 +190,17 @@ func getBlockForMining(mineWhenNotSynced bool) *externalapi.DomainBlock {
|
|||||||
|
|
||||||
func templatesLoop(client *minerClient, miningAddr util.Address, errChan chan error) {
|
func templatesLoop(client *minerClient, miningAddr util.Address, errChan chan error) {
|
||||||
getBlockTemplate := func() {
|
getBlockTemplate := func() {
|
||||||
template, err := client.GetBlockTemplate(miningAddr.String())
|
template, err := client.safeRPCClient().GetBlockTemplate(miningAddr.String())
|
||||||
if nativeerrors.Is(err, router.ErrTimeout) {
|
if nativeerrors.Is(err, router.ErrTimeout) {
|
||||||
log.Warnf("Got timeout while requesting block template from %s: %s", client.Address(), err)
|
log.Warnf("Got timeout while requesting block template from %s: %s", client.safeRPCClient().Address(), err)
|
||||||
reconnectErr := client.Reconnect()
|
client.reconnect()
|
||||||
if reconnectErr != nil {
|
|
||||||
errChan <- reconnectErr
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if nativeerrors.Is(err, router.ErrRouteClosed) {
|
|
||||||
log.Debugf("Got route is closed while requesting block template from %s. "+
|
|
||||||
"The client is most likely reconnecting", client.Address())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errChan <- errors.Wrapf(err, "Error getting block template from %s", client.Address())
|
errChan <- errors.Wrapf(err, "Error getting block template from %s", client.safeRPCClient().Address())
|
||||||
return
|
|
||||||
}
|
|
||||||
err = templatemanager.Set(template)
|
|
||||||
if err != nil {
|
|
||||||
errChan <- errors.Wrapf(err, "Error setting block template from %s", client.Address())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
templatemanager.Set(template)
|
||||||
}
|
}
|
||||||
|
|
||||||
getBlockTemplate()
|
getBlockTemplate()
|
||||||
|
|||||||
@@ -23,14 +23,10 @@ func Get() (*externalapi.DomainBlock, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set sets the current template to work on
|
// Set sets the current template to work on
|
||||||
func Set(template *appmessage.GetBlockTemplateResponseMessage) error {
|
func Set(template *appmessage.GetBlockTemplateResponseMessage) {
|
||||||
block, err := appmessage.RPCBlockToDomainBlock(template.Block)
|
block := appmessage.MsgBlockToDomainBlock(template.MsgBlock)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
lock.Lock()
|
lock.Lock()
|
||||||
defer lock.Unlock()
|
defer lock.Unlock()
|
||||||
currentTemplate = block
|
currentTemplate = block
|
||||||
isSynced = template.IsSynced
|
isSynced = template.IsSynced
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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 {
|
|
||||||
}
|
|
||||||
@@ -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",
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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()
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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,
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
package bip32
|
|
||||||
|
|
||||||
import "crypto/rand"
|
|
||||||
|
|
||||||
// GenerateSeed generates seed that can be used to initialize a master key.
|
|
||||||
func GenerateSeed() ([]byte, error) {
|
|
||||||
randBytes := make([]byte, 32)
|
|
||||||
_, err := rand.Read(randBytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return randBytes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMasterWithPath returns a new master key based on the given seed and version, with a derivation
|
|
||||||
// to the given path.
|
|
||||||
func NewMasterWithPath(seed []byte, version [4]byte, pathString string) (*ExtendedKey, error) {
|
|
||||||
masterKey, err := NewMaster(seed, version)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return masterKey.DeriveFromPath(pathString)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPublicMasterWithPath returns a new public master key based on the given seed and version, with a derivation
|
|
||||||
// to the given path.
|
|
||||||
func NewPublicMasterWithPath(seed []byte, version [4]byte, pathString string) (*ExtendedKey, error) {
|
|
||||||
masterKey, err := NewMaster(seed, version)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
path, err := parsePath(pathString)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
descendantKey, err := masterKey.path(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return descendantKey.Public()
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user