Merge branch 'dev' into bug-block-verbosedata

This commit is contained in:
Michael Sutton 2024-05-09 22:13:21 +03:00 committed by GitHub
commit f8dec0e81f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 555 additions and 176 deletions

View File

@ -28,7 +28,8 @@ func HandleSubmitTransaction(context *rpccontext.Context, _ *router.Router, requ
} }
log.Debugf("Rejected transaction %s: %s", transactionID, err) log.Debugf("Rejected transaction %s: %s", transactionID, err)
errorMessage := &appmessage.SubmitTransactionResponseMessage{} // Return the ID also in the case of error, so that clients can match the response to the correct transaction submit request
errorMessage := appmessage.NewSubmitTransactionResponseMessage(transactionID.String())
errorMessage.Error = appmessage.RPCErrorf("Rejected transaction %s: %s", transactionID, err) errorMessage.Error = appmessage.RPCErrorf("Rejected transaction %s: %s", transactionID, err)
return errorMessage, nil return errorMessage, nil
} }

View File

@ -1,3 +1,30 @@
Kaspad v0.12.17 - 2024-02-19
===========================
* Wallet-related improvements and fixes (#2253, #2257, #2258, #2262)
Kaspad v0.12.16 - 2023-12-25
===========================
* Adapt wallet UTXO selection to dust patch (#2254)
Kaspad v0.12.15 - 2023-12-16
===========================
* Update ECDSA address test to use a valid public key (#2202)
* Fix off by small amounts in sent amount kaspa (#2220)
* Use removeRedeemers correctly by (#2235)
* Fix windows asset building by increasing go version (#2245)
* Added a mainnet dnsseeder (#2247)
* Fix extract atomic swap data pushes (#2203)
* Adapt kaspawallet to support testnet 11 (#2211)
* Fix type detection in RemoveInvalidTransactions (#2252)
Kaspad v0.12.14 - 2023-09-26
===========================
* Anti-spam measurements against dust attack (#2223)
Kaspad v0.12.13 - 2023-03-06 Kaspad v0.12.13 - 2023-03-06
=========================== ===========================

View File

@ -1,10 +1,9 @@
package main package main
import ( import (
"os"
"github.com/kaspanet/kaspad/infrastructure/config" "github.com/kaspanet/kaspad/infrastructure/config"
"github.com/pkg/errors" "github.com/pkg/errors"
"os"
"github.com/jessevdk/go-flags" "github.com/jessevdk/go-flags"
) )
@ -22,6 +21,8 @@ const (
newAddressSubCmd = "new-address" newAddressSubCmd = "new-address"
dumpUnencryptedDataSubCmd = "dump-unencrypted-data" dumpUnencryptedDataSubCmd = "dump-unencrypted-data"
startDaemonSubCmd = "start-daemon" startDaemonSubCmd = "start-daemon"
versionSubCmd = "version"
getDaemonVersionSubCmd = "get-daemon-version"
) )
const ( const (
@ -30,6 +31,7 @@ const (
) )
type configFlags struct { type configFlags struct {
ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"`
config.NetworkFlags config.NetworkFlags
} }
@ -56,9 +58,9 @@ type sendConfig struct {
Password string `long:"password" short:"p" description:"Wallet password"` Password string `long:"password" short:"p" description:"Wallet password"`
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to"` DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to"`
ToAddress string `long:"to-address" short:"t" description:"The public address to send Kaspa to" required:"true"` ToAddress string `long:"to-address" short:"t" description:"The public address to send Kaspa to" required:"true"`
FromAddresses []string `long:"from-address" short:"a" description:"Specific public address to send Kaspa from. Use multiple times to accept several addresses" required:"false"` FromAddresses []string `long:"from-address" short:"a" description:"Specific public address to send Kaspa from. Repeat multiple times (adding -a before each) to accept several addresses" required:"false"`
SendAmount string `long:"send-amount" short:"v" description:"An amount to send in Kaspa (e.g. 1234.12345678)"` SendAmount string `long:"send-amount" short:"v" description:"An amount to send in Kaspa (e.g. 1234.12345678)"`
IsSendAll bool `long:"send-all" description:"Send all the Kaspa in the wallet (mutually exclusive with --send-amount)"` IsSendAll bool `long:"send-all" description:"Send all the Kaspa in the wallet (mutually exclusive with --send-amount). If --from-address was used, will send all only from the specified addresses."`
UseExistingChangeAddress bool `long:"use-existing-change-address" short:"u" description:"Will use an existing change address (in case no change address was ever used, it will use a new one)"` UseExistingChangeAddress bool `long:"use-existing-change-address" short:"u" description:"Will use an existing change address (in case no change address was ever used, it will use a new one)"`
Verbose bool `long:"show-serialized" short:"s" description:"Show a list of hex encoded sent transactions"` Verbose bool `long:"show-serialized" short:"s" description:"Show a list of hex encoded sent transactions"`
config.NetworkFlags config.NetworkFlags
@ -129,6 +131,13 @@ type dumpUnencryptedDataConfig struct {
config.NetworkFlags config.NetworkFlags
} }
type versionConfig struct {
}
type getDaemonVersionConfig struct {
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to"`
}
func parseCommandLine() (subCommand string, config interface{}) { func parseCommandLine() (subCommand string, config interface{}) {
cfg := &configFlags{} cfg := &configFlags{}
parser := flags.NewParser(cfg, flags.PrintErrors|flags.HelpFlag) parser := flags.NewParser(cfg, flags.PrintErrors|flags.HelpFlag)
@ -185,6 +194,9 @@ func parseCommandLine() (subCommand string, config interface{}) {
Listen: defaultListen, Listen: defaultListen,
} }
parser.AddCommand(startDaemonSubCmd, "Start the wallet daemon", "Start the wallet daemon", startDaemonConf) parser.AddCommand(startDaemonSubCmd, "Start the wallet daemon", "Start the wallet daemon", startDaemonConf)
parser.AddCommand(versionSubCmd, "Get the wallet version", "Get the wallet version", &versionConfig{})
getDaemonVersionConf := &getDaemonVersionConfig{DaemonAddress: defaultListen}
parser.AddCommand(getDaemonVersionSubCmd, "Get the wallet daemon version", "Get the wallet daemon version", getDaemonVersionConf)
_, err := parser.Parse() _, err := parser.Parse()
if err != nil { if err != nil {
@ -290,6 +302,9 @@ func parseCommandLine() (subCommand string, config interface{}) {
printErrorAndExit(err) printErrorAndExit(err)
} }
config = startDaemonConf config = startDaemonConf
case versionSubCmd:
case getDaemonVersionSubCmd:
config = getDaemonVersionConf
} }
return parser.Command.Active.Name, config return parser.Command.Active.Name, config

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.26.0 // protoc-gen-go v1.27.1
// protoc v3.21.12 // protoc v3.12.3
// source: kaspawalletd.proto // source: kaspawalletd.proto
package pb package pb
@ -1242,6 +1242,91 @@ func (x *SignResponse) GetSignedTransactions() [][]byte {
return nil return nil
} }
type GetVersionRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *GetVersionRequest) Reset() {
*x = GetVersionRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_kaspawalletd_proto_msgTypes[23]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetVersionRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetVersionRequest) ProtoMessage() {}
func (x *GetVersionRequest) ProtoReflect() protoreflect.Message {
mi := &file_kaspawalletd_proto_msgTypes[23]
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 GetVersionRequest.ProtoReflect.Descriptor instead.
func (*GetVersionRequest) Descriptor() ([]byte, []int) {
return file_kaspawalletd_proto_rawDescGZIP(), []int{23}
}
type GetVersionResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"`
}
func (x *GetVersionResponse) Reset() {
*x = GetVersionResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_kaspawalletd_proto_msgTypes[24]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetVersionResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetVersionResponse) ProtoMessage() {}
func (x *GetVersionResponse) ProtoReflect() protoreflect.Message {
mi := &file_kaspawalletd_proto_msgTypes[24]
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 GetVersionResponse.ProtoReflect.Descriptor instead.
func (*GetVersionResponse) Descriptor() ([]byte, []int) {
return file_kaspawalletd_proto_rawDescGZIP(), []int{24}
}
func (x *GetVersionResponse) GetVersion() string {
if x != nil {
return x.Version
}
return ""
}
var File_kaspawalletd_proto protoreflect.FileDescriptor var File_kaspawalletd_proto protoreflect.FileDescriptor
var file_kaspawalletd_proto_rawDesc = []byte{ var file_kaspawalletd_proto_rawDesc = []byte{
@ -1371,62 +1456,72 @@ var file_kaspawalletd_proto_rawDesc = []byte{
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x12, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x12, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72,
0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c,
0x52, 0x12, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x52, 0x12, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x73, 0x32, 0xb3, 0x06, 0x0a, 0x0c, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x13, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69,
0x6c, 0x6c, 0x65, 0x74, 0x64, 0x12, 0x51, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x2e, 0x0a, 0x12, 0x47, 0x65, 0x74,
0x6e, 0x63, 0x65, 0x12, 0x1f, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x74, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x32, 0x86, 0x07, 0x0a, 0x0c, 0x6b, 0x61,
0x65, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x12, 0x51, 0x0a, 0x0a, 0x47, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7e, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x45, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1f, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61,
0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e,
0x55, 0x54, 0x58, 0x4f, 0x73, 0x12, 0x2e, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6b, 0x61, 0x73, 0x70,
0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61,
0x53, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x73, 0x52, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7e, 0x0a,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x19, 0x47, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x70, 0x65, 0x6e,
0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x73, 0x12, 0x2e, 0x2e, 0x6b, 0x61, 0x73,
0x53, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x73, 0x52, 0x65, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x74,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x81, 0x01, 0x0a, 0x1a, 0x43, 0x72, 0x65, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x54,
0x58, 0x4f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x6b, 0x61, 0x73,
0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x74,
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x54,
0x58, 0x4f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x81, 0x01,
0x0a, 0x1a, 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, 0x73, 0x12, 0x2f, 0x2e, 0x6b,
0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 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, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e,
0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x43, 0x72, 0x65,
0x61, 0x74, 0x65, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73,
0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2f, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x73, 0x00, 0x12, 0x5a, 0x0a, 0x0d, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x73, 0x12, 0x22, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74,
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x64, 0x2e, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52,
0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61,
0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x0d, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a,
0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x22, 0x2e, 0x0a, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x2e, 0x6b, 0x61,
0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x68, 0x6f, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64,
0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6b,
0x74, 0x1a, 0x23, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x4e, 0x65, 0x77, 0x41,
0x2e, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x0a, 0x4e, 0x65, 0x77, 0x41, 0x12, 0x4b, 0x0a, 0x08, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x1d, 0x2e, 0x6b,
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x68, 0x75, 0x74,
0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6b, 0x61,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64,
0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a,
0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x08, 0x53, 0x09, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x12, 0x1e, 0x2e, 0x6b, 0x61, 0x73,
0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x1d, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63,
0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6b, 0x61, 0x73,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63,
0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x61, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x09, 0x42, 0x72, 0x6f, 0x61, 0x04, 0x53, 0x65, 0x6e, 0x64, 0x12, 0x19, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c,
0x64, 0x63, 0x61, 0x73, 0x74, 0x12, 0x1e, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x6c, 0x65, 0x74, 0x64, 0x2e, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x1a, 0x1a, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3f,
0x6c, 0x65, 0x74, 0x64, 0x2e, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x0a, 0x04, 0x53, 0x69, 0x67, 0x6e, 0x12, 0x19, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x04, 0x53, 0x65, 0x6e, 0x64, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x12, 0x19, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x74, 0x1a, 0x1a, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64,
0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6b, 0x61, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x51, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x04, 0x53, 0x69, 0x67, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74,
0x6e, 0x12, 0x19, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20,
0x2e, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6b, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65,
0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x22, 0x00, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x6e, 0x65, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x6e, 0x65, 0x74, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x64,
0x74, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x64, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x6b, 0x61, 0x73, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74,
0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2f, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2f, 0x2f, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x6f, 0x33,
} }
var ( var (
@ -1441,7 +1536,7 @@ func file_kaspawalletd_proto_rawDescGZIP() []byte {
return file_kaspawalletd_proto_rawDescData return file_kaspawalletd_proto_rawDescData
} }
var file_kaspawalletd_proto_msgTypes = make([]protoimpl.MessageInfo, 23) var file_kaspawalletd_proto_msgTypes = make([]protoimpl.MessageInfo, 25)
var file_kaspawalletd_proto_goTypes = []interface{}{ var file_kaspawalletd_proto_goTypes = []interface{}{
(*GetBalanceRequest)(nil), // 0: kaspawalletd.GetBalanceRequest (*GetBalanceRequest)(nil), // 0: kaspawalletd.GetBalanceRequest
(*GetBalanceResponse)(nil), // 1: kaspawalletd.GetBalanceResponse (*GetBalanceResponse)(nil), // 1: kaspawalletd.GetBalanceResponse
@ -1466,6 +1561,8 @@ var file_kaspawalletd_proto_goTypes = []interface{}{
(*SendResponse)(nil), // 20: kaspawalletd.SendResponse (*SendResponse)(nil), // 20: kaspawalletd.SendResponse
(*SignRequest)(nil), // 21: kaspawalletd.SignRequest (*SignRequest)(nil), // 21: kaspawalletd.SignRequest
(*SignResponse)(nil), // 22: kaspawalletd.SignResponse (*SignResponse)(nil), // 22: kaspawalletd.SignResponse
(*GetVersionRequest)(nil), // 23: kaspawalletd.GetVersionRequest
(*GetVersionResponse)(nil), // 24: kaspawalletd.GetVersionResponse
} }
var file_kaspawalletd_proto_depIdxs = []int32{ var file_kaspawalletd_proto_depIdxs = []int32{
2, // 0: kaspawalletd.GetBalanceResponse.addressBalances:type_name -> kaspawalletd.AddressBalances 2, // 0: kaspawalletd.GetBalanceResponse.addressBalances:type_name -> kaspawalletd.AddressBalances
@ -1482,17 +1579,19 @@ var file_kaspawalletd_proto_depIdxs = []int32{
9, // 11: kaspawalletd.kaspawalletd.Broadcast:input_type -> kaspawalletd.BroadcastRequest 9, // 11: kaspawalletd.kaspawalletd.Broadcast:input_type -> kaspawalletd.BroadcastRequest
19, // 12: kaspawalletd.kaspawalletd.Send:input_type -> kaspawalletd.SendRequest 19, // 12: kaspawalletd.kaspawalletd.Send:input_type -> kaspawalletd.SendRequest
21, // 13: kaspawalletd.kaspawalletd.Sign:input_type -> kaspawalletd.SignRequest 21, // 13: kaspawalletd.kaspawalletd.Sign:input_type -> kaspawalletd.SignRequest
1, // 14: kaspawalletd.kaspawalletd.GetBalance:output_type -> kaspawalletd.GetBalanceResponse 23, // 14: kaspawalletd.kaspawalletd.GetVersion:input_type -> kaspawalletd.GetVersionRequest
18, // 15: kaspawalletd.kaspawalletd.GetExternalSpendableUTXOs:output_type -> kaspawalletd.GetExternalSpendableUTXOsResponse 1, // 15: kaspawalletd.kaspawalletd.GetBalance:output_type -> kaspawalletd.GetBalanceResponse
4, // 16: kaspawalletd.kaspawalletd.CreateUnsignedTransactions:output_type -> kaspawalletd.CreateUnsignedTransactionsResponse 18, // 16: kaspawalletd.kaspawalletd.GetExternalSpendableUTXOs:output_type -> kaspawalletd.GetExternalSpendableUTXOsResponse
6, // 17: kaspawalletd.kaspawalletd.ShowAddresses:output_type -> kaspawalletd.ShowAddressesResponse 4, // 17: kaspawalletd.kaspawalletd.CreateUnsignedTransactions:output_type -> kaspawalletd.CreateUnsignedTransactionsResponse
8, // 18: kaspawalletd.kaspawalletd.NewAddress:output_type -> kaspawalletd.NewAddressResponse 6, // 18: kaspawalletd.kaspawalletd.ShowAddresses:output_type -> kaspawalletd.ShowAddressesResponse
12, // 19: kaspawalletd.kaspawalletd.Shutdown:output_type -> kaspawalletd.ShutdownResponse 8, // 19: kaspawalletd.kaspawalletd.NewAddress:output_type -> kaspawalletd.NewAddressResponse
10, // 20: kaspawalletd.kaspawalletd.Broadcast:output_type -> kaspawalletd.BroadcastResponse 12, // 20: kaspawalletd.kaspawalletd.Shutdown:output_type -> kaspawalletd.ShutdownResponse
20, // 21: kaspawalletd.kaspawalletd.Send:output_type -> kaspawalletd.SendResponse 10, // 21: kaspawalletd.kaspawalletd.Broadcast:output_type -> kaspawalletd.BroadcastResponse
22, // 22: kaspawalletd.kaspawalletd.Sign:output_type -> kaspawalletd.SignResponse 20, // 22: kaspawalletd.kaspawalletd.Send:output_type -> kaspawalletd.SendResponse
14, // [14:23] is the sub-list for method output_type 22, // 23: kaspawalletd.kaspawalletd.Sign:output_type -> kaspawalletd.SignResponse
5, // [5:14] is the sub-list for method input_type 24, // 24: kaspawalletd.kaspawalletd.GetVersion:output_type -> kaspawalletd.GetVersionResponse
15, // [15:25] is the sub-list for method output_type
5, // [5:15] is the sub-list for method input_type
5, // [5:5] is the sub-list for extension type_name 5, // [5:5] is the sub-list for extension type_name
5, // [5:5] is the sub-list for extension extendee 5, // [5:5] is the sub-list for extension extendee
0, // [0:5] is the sub-list for field type_name 0, // [0:5] is the sub-list for field type_name
@ -1780,6 +1879,30 @@ func file_kaspawalletd_proto_init() {
return nil return nil
} }
} }
file_kaspawalletd_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetVersionRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_kaspawalletd_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetVersionResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
} }
type x struct{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
@ -1787,7 +1910,7 @@ func file_kaspawalletd_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_kaspawalletd_proto_rawDesc, RawDescriptor: file_kaspawalletd_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 23, NumMessages: 25,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },

View File

@ -15,6 +15,7 @@ service kaspawalletd {
rpc Send(SendRequest) returns (SendResponse) {} rpc Send(SendRequest) returns (SendResponse) {}
// Since SignRequest contains a password - this command should only be used on a trusted or secure connection // Since SignRequest contains a password - this command should only be used on a trusted or secure connection
rpc Sign(SignRequest) returns (SignResponse) {} rpc Sign(SignRequest) returns (SignResponse) {}
rpc GetVersion(GetVersionRequest) returns (GetVersionResponse) {}
} }
message GetBalanceRequest { message GetBalanceRequest {
@ -127,3 +128,10 @@ message SignRequest{
message SignResponse{ message SignResponse{
repeated bytes signedTransactions = 1; repeated bytes signedTransactions = 1;
} }
message GetVersionRequest{
}
message GetVersionResponse{
string version = 1;
}

View File

@ -1,4 +1,8 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.12.3
// source: kaspawalletd.proto
package pb package pb
@ -29,6 +33,7 @@ type KaspawalletdClient interface {
Send(ctx context.Context, in *SendRequest, opts ...grpc.CallOption) (*SendResponse, error) Send(ctx context.Context, in *SendRequest, opts ...grpc.CallOption) (*SendResponse, error)
// Since SignRequest contains a password - this command should only be used on a trusted or secure connection // Since SignRequest contains a password - this command should only be used on a trusted or secure connection
Sign(ctx context.Context, in *SignRequest, opts ...grpc.CallOption) (*SignResponse, error) Sign(ctx context.Context, in *SignRequest, opts ...grpc.CallOption) (*SignResponse, error)
GetVersion(ctx context.Context, in *GetVersionRequest, opts ...grpc.CallOption) (*GetVersionResponse, error)
} }
type kaspawalletdClient struct { type kaspawalletdClient struct {
@ -120,6 +125,15 @@ func (c *kaspawalletdClient) Sign(ctx context.Context, in *SignRequest, opts ...
return out, nil return out, nil
} }
func (c *kaspawalletdClient) GetVersion(ctx context.Context, in *GetVersionRequest, opts ...grpc.CallOption) (*GetVersionResponse, error) {
out := new(GetVersionResponse)
err := c.cc.Invoke(ctx, "/kaspawalletd.kaspawalletd/GetVersion", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// KaspawalletdServer is the server API for Kaspawalletd service. // KaspawalletdServer is the server API for Kaspawalletd service.
// All implementations must embed UnimplementedKaspawalletdServer // All implementations must embed UnimplementedKaspawalletdServer
// for forward compatibility // for forward compatibility
@ -135,6 +149,7 @@ type KaspawalletdServer interface {
Send(context.Context, *SendRequest) (*SendResponse, error) Send(context.Context, *SendRequest) (*SendResponse, error)
// Since SignRequest contains a password - this command should only be used on a trusted or secure connection // Since SignRequest contains a password - this command should only be used on a trusted or secure connection
Sign(context.Context, *SignRequest) (*SignResponse, error) Sign(context.Context, *SignRequest) (*SignResponse, error)
GetVersion(context.Context, *GetVersionRequest) (*GetVersionResponse, error)
mustEmbedUnimplementedKaspawalletdServer() mustEmbedUnimplementedKaspawalletdServer()
} }
@ -169,6 +184,9 @@ func (UnimplementedKaspawalletdServer) Send(context.Context, *SendRequest) (*Sen
func (UnimplementedKaspawalletdServer) Sign(context.Context, *SignRequest) (*SignResponse, error) { func (UnimplementedKaspawalletdServer) Sign(context.Context, *SignRequest) (*SignResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Sign not implemented") return nil, status.Errorf(codes.Unimplemented, "method Sign not implemented")
} }
func (UnimplementedKaspawalletdServer) GetVersion(context.Context, *GetVersionRequest) (*GetVersionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetVersion not implemented")
}
func (UnimplementedKaspawalletdServer) mustEmbedUnimplementedKaspawalletdServer() {} func (UnimplementedKaspawalletdServer) mustEmbedUnimplementedKaspawalletdServer() {}
// UnsafeKaspawalletdServer may be embedded to opt out of forward compatibility for this service. // UnsafeKaspawalletdServer may be embedded to opt out of forward compatibility for this service.
@ -344,6 +362,24 @@ func _Kaspawalletd_Sign_Handler(srv interface{}, ctx context.Context, dec func(i
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _Kaspawalletd_GetVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetVersionRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(KaspawalletdServer).GetVersion(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/kaspawalletd.kaspawalletd/GetVersion",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(KaspawalletdServer).GetVersion(ctx, req.(*GetVersionRequest))
}
return interceptor(ctx, in, info, handler)
}
// Kaspawalletd_ServiceDesc is the grpc.ServiceDesc for Kaspawalletd service. // Kaspawalletd_ServiceDesc is the grpc.ServiceDesc for Kaspawalletd service.
// It's only intended for direct use with grpc.RegisterService, // It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy) // and not to be introspected or modified (even as a copy)
@ -387,6 +423,10 @@ var Kaspawalletd_ServiceDesc = grpc.ServiceDesc{
MethodName: "Sign", MethodName: "Sign",
Handler: _Kaspawalletd_Sign_Handler, Handler: _Kaspawalletd_Sign_Handler,
}, },
{
MethodName: "GetVersion",
Handler: _Kaspawalletd_GetVersion_Handler,
},
}, },
Streams: []grpc.StreamDesc{}, Streams: []grpc.StreamDesc{},
Metadata: "kaspawalletd.proto", Metadata: "kaspawalletd.proto",

View File

@ -2,6 +2,7 @@ package server
import ( import (
"context" "context"
"github.com/pkg/errors"
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb" "github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet" "github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
@ -14,13 +15,15 @@ func (s *server) GetBalance(_ context.Context, _ *pb.GetBalanceRequest) (*pb.Get
s.lock.RLock() s.lock.RLock()
defer s.lock.RUnlock() defer s.lock.RUnlock()
if !s.isSynced() {
return nil, errors.Errorf("wallet daemon is not synced yet, %s", s.formatSyncStateReport())
}
dagInfo, err := s.rpcClient.GetBlockDAGInfo() dagInfo, err := s.rpcClient.GetBlockDAGInfo()
if err != nil { if err != nil {
return nil, err return nil, err
} }
daaScore := dagInfo.VirtualDAAScore daaScore := dagInfo.VirtualDAAScore
maturity := s.params.BlockCoinbaseMaturity
balancesMap := make(balancesMapType, 0) balancesMap := make(balancesMapType, 0)
for _, entry := range s.utxosSortedByAmount { for _, entry := range s.utxosSortedByAmount {
amount := entry.UTXOEntry.Amount() amount := entry.UTXOEntry.Amount()
@ -30,7 +33,7 @@ func (s *server) GetBalance(_ context.Context, _ *pb.GetBalanceRequest) (*pb.Get
balances = new(balancesType) balances = new(balancesType)
balancesMap[address] = balances balancesMap[address] = balances
} }
if isUTXOSpendable(entry, daaScore, maturity) { if s.isUTXOSpendable(entry, daaScore) {
balances.available += amount balances.available += amount
} else { } else {
balances.pending += amount balances.pending += amount
@ -55,6 +58,8 @@ func (s *server) GetBalance(_ context.Context, _ *pb.GetBalanceRequest) (*pb.Get
pending += balances.pending pending += balances.pending
} }
log.Infof("GetBalance request scanned %d UTXOs overall over %d addresses", len(s.utxosSortedByAmount), len(balancesMap))
return &pb.GetBalanceResponse{ return &pb.GetBalanceResponse{
Available: available, Available: available,
Pending: pending, Pending: pending,
@ -62,9 +67,9 @@ func (s *server) GetBalance(_ context.Context, _ *pb.GetBalanceRequest) (*pb.Get
}, nil }, nil
} }
func isUTXOSpendable(entry *walletUTXO, virtualDAAScore uint64, coinbaseMaturity uint64) bool { func (s *server) isUTXOSpendable(entry *walletUTXO, virtualDAAScore uint64) bool {
if !entry.UTXOEntry.IsCoinbase() { if !entry.UTXOEntry.IsCoinbase() {
return true return true
} }
return entry.UTXOEntry.BlockDAAScore()+coinbaseMaturity < virtualDAAScore return entry.UTXOEntry.BlockDAAScore()+s.coinbaseMaturity < virtualDAAScore
} }

View File

@ -2,14 +2,16 @@ package server
import ( import (
"context" "context"
"time"
"github.com/kaspanet/kaspad/app/appmessage" "github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb" "github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet" "github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet/serialization" "github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet/serialization"
"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/infrastructure/network/rpcclient" "github.com/kaspanet/kaspad/infrastructure/network/rpcclient"
"github.com/pkg/errors" "github.com/pkg/errors"
"time"
) )
func (s *server) Broadcast(_ context.Context, request *pb.BroadcastRequest) (*pb.BroadcastResponse, error) { func (s *server) Broadcast(_ context.Context, request *pb.BroadcastRequest) (*pb.BroadcastResponse, error) {
@ -54,16 +56,12 @@ func (s *server) broadcast(transactions [][]byte, isDomain bool) ([]string, erro
} }
} }
err = s.refreshUTXOs() s.forceSync()
if err != nil {
return nil, err
}
return txIDs, nil return txIDs, nil
} }
func sendTransaction(client *rpcclient.RPCClient, tx *externalapi.DomainTransaction) (string, error) { func sendTransaction(client *rpcclient.RPCClient, tx *externalapi.DomainTransaction) (string, error) {
submitTransactionResponse, err := client.SubmitTransaction(appmessage.DomainTransactionToRPCTransaction(tx), false) submitTransactionResponse, err := client.SubmitTransaction(appmessage.DomainTransactionToRPCTransaction(tx), consensushashing.TransactionID(tx).String(), false)
if err != nil { if err != nil {
return "", errors.Wrapf(err, "error submitting transaction") return "", errors.Wrapf(err, "error submitting transaction")
} }

View File

@ -3,19 +3,22 @@ package server
import ( import (
"context" "context"
"fmt" "fmt"
"time"
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb" "github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet" "github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants" "github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"github.com/kaspanet/kaspad/util" "github.com/kaspanet/kaspad/util"
"github.com/pkg/errors" "github.com/pkg/errors"
"golang.org/x/exp/slices"
) )
// TODO: Implement a better fee estimation mechanism // TODO: Implement a better fee estimation mechanism
const feePerInput = 10000 const feePerInput = 10000
// The minimal change amount to target in order to avoid large storage mass (see KIP9 for more details).
// By having at least 0.2KAS in the change output we make sure that every transaction with send value >= 0.2KAS
// should succeed (at most 50K storage mass for each output, thus overall lower than standard mass upper bound which is 100K gram)
const minChangeTarget = constants.SompiPerKaspa / 5
func (s *server) CreateUnsignedTransactions(_ context.Context, request *pb.CreateUnsignedTransactionsRequest) ( func (s *server) CreateUnsignedTransactions(_ context.Context, request *pb.CreateUnsignedTransactionsRequest) (
*pb.CreateUnsignedTransactionsResponse, error, *pb.CreateUnsignedTransactionsResponse, error,
) { ) {
@ -35,7 +38,6 @@ func (s *server) createUnsignedTransactions(address string, amount uint64, isSen
if !s.isSynced() { if !s.isSynced() {
return nil, errors.Errorf("wallet daemon is not synced yet, %s", s.formatSyncStateReport()) return nil, errors.Errorf("wallet daemon is not synced yet, %s", s.formatSyncStateReport())
} }
// make sure address string is correct before proceeding to a // make sure address string is correct before proceeding to a
// potentially long UTXO refreshment operation // potentially long UTXO refreshment operation
toAddress, err := util.DecodeAddress(address, s.params.Prefix) toAddress, err := util.DecodeAddress(address, s.params.Prefix)
@ -43,16 +45,11 @@ func (s *server) createUnsignedTransactions(address string, amount uint64, isSen
return nil, err return nil, err
} }
err = s.refreshUTXOs()
if err != nil {
return nil, err
}
var fromAddresses []*walletAddress var fromAddresses []*walletAddress
for _, from := range fromAddressesString { for _, from := range fromAddressesString {
fromAddress, exists := s.addressSet[from] fromAddress, exists := s.addressSet[from]
if !exists { if !exists {
return nil, fmt.Errorf("Specified from address %s does not exists", from) return nil, fmt.Errorf("specified from address %s does not exists", from)
} }
fromAddresses = append(fromAddresses, fromAddress) fromAddresses = append(fromAddresses, fromAddress)
} }
@ -106,19 +103,14 @@ func (s *server) selectUTXOs(spendAmount uint64, isSendAll bool, feePerInput uin
return nil, 0, 0, err return nil, 0, 0, err
} }
coinbaseMaturity := s.params.BlockCoinbaseMaturity
if dagInfo.NetworkName == "kaspa-testnet-11" {
coinbaseMaturity = 1000
}
for _, utxo := range s.utxosSortedByAmount { for _, utxo := range s.utxosSortedByAmount {
if (fromAddresses != nil && !slices.Contains(fromAddresses, utxo.address)) || if (fromAddresses != nil && !walletAddressesContain(fromAddresses, utxo.address)) ||
!isUTXOSpendable(utxo, dagInfo.VirtualDAAScore, coinbaseMaturity) { !s.isUTXOSpendable(utxo, dagInfo.VirtualDAAScore) {
continue continue
} }
if broadcastTime, ok := s.usedOutpoints[*utxo.Outpoint]; ok { if broadcastTime, ok := s.usedOutpoints[*utxo.Outpoint]; ok {
if time.Since(broadcastTime) > time.Minute { if s.usedOutpointHasExpired(broadcastTime) {
delete(s.usedOutpoints, *utxo.Outpoint) delete(s.usedOutpoints, *utxo.Outpoint)
} else { } else {
continue continue
@ -135,7 +127,12 @@ func (s *server) selectUTXOs(spendAmount uint64, isSendAll bool, feePerInput uin
fee := feePerInput * uint64(len(selectedUTXOs)) fee := feePerInput * uint64(len(selectedUTXOs))
totalSpend := spendAmount + fee totalSpend := spendAmount + fee
if !isSendAll && totalValue >= totalSpend { // Two break cases (if not send all):
// 1. totalValue == totalSpend, so there's no change needed -> number of outputs = 1, so a single input is sufficient
// 2. totalValue > totalSpend, so there will be change and 2 outputs, therefor in order to not struggle with --
// 2.1 go-nodes dust patch we try and find at least 2 inputs (even though the next one is not necessary in terms of spend value)
// 2.2 KIP9 we try and make sure that the change amount is not too small
if !isSendAll && (totalValue == totalSpend || (totalValue >= totalSpend+minChangeTarget && len(selectedUTXOs) > 1)) {
break break
} }
} }
@ -156,3 +153,13 @@ func (s *server) selectUTXOs(spendAmount uint64, isSendAll bool, feePerInput uin
return selectedUTXOs, totalReceived, totalValue - totalSpend, nil return selectedUTXOs, totalReceived, totalValue - totalSpend, nil
} }
func walletAddressesContain(addresses []*walletAddress, contain *walletAddress) bool {
for _, address := range addresses {
if *address == *contain {
return true
}
}
return false
}

View File

@ -5,8 +5,11 @@ import (
"net" "net"
"os" "os"
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/kaspanet/kaspad/version"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/util/txmass" "github.com/kaspanet/kaspad/util/txmass"
@ -27,17 +30,22 @@ import (
type server struct { type server struct {
pb.UnimplementedKaspawalletdServer pb.UnimplementedKaspawalletdServer
rpcClient *rpcclient.RPCClient rpcClient *rpcclient.RPCClient // RPC client for ongoing user requests
params *dagconfig.Params backgroundRPCClient *rpcclient.RPCClient // RPC client dedicated for address and UTXO background fetching
params *dagconfig.Params
coinbaseMaturity uint64 // Is different from default if we use testnet-11
lock sync.RWMutex lock sync.RWMutex
utxosSortedByAmount []*walletUTXO utxosSortedByAmount []*walletUTXO
nextSyncStartIndex uint32 nextSyncStartIndex uint32
keysFile *keys.File keysFile *keys.File
shutdown chan struct{} shutdown chan struct{}
addressSet walletAddressSet forceSyncChan chan struct{}
txMassCalculator *txmass.Calculator startTimeOfLastCompletedRefresh time.Time
usedOutpoints map[externalapi.DomainOutpoint]time.Time addressSet walletAddressSet
txMassCalculator *txmass.Calculator
usedOutpoints map[externalapi.DomainOutpoint]time.Time
firstSyncDone atomic.Bool
isLogFinalProgressLineShown bool isLogFinalProgressLineShown bool
maxUsedAddressesForLog uint32 maxUsedAddressesForLog uint32
@ -59,6 +67,7 @@ func Start(params *dagconfig.Params, listen, rpcServer string, keysFilePath stri
profiling.Start(profile, log) profiling.Start(profile, log)
} }
log.Infof("Version %s", version.Version())
listener, err := net.Listen("tcp", listen) listener, err := net.Listen("tcp", listen)
if err != nil { if err != nil {
return (errors.Wrapf(err, "Error listening to TCP on %s", listen)) return (errors.Wrapf(err, "Error listening to TCP on %s", listen))
@ -70,6 +79,10 @@ func Start(params *dagconfig.Params, listen, rpcServer string, keysFilePath stri
if err != nil { if err != nil {
return (errors.Wrapf(err, "Error connecting to RPC server %s", rpcServer)) return (errors.Wrapf(err, "Error connecting to RPC server %s", rpcServer))
} }
backgroundRPCClient, err := connectToRPC(params, rpcServer, timeout)
if err != nil {
return (errors.Wrapf(err, "Error making a second connection to RPC server %s", rpcServer))
}
log.Infof("Connected, reading keys file %s...", keysFilePath) log.Infof("Connected, reading keys file %s...", keysFilePath)
keysFile, err := keys.ReadKeysFile(params, keysFilePath) keysFile, err := keys.ReadKeysFile(params, keysFilePath)
@ -82,13 +95,26 @@ func Start(params *dagconfig.Params, listen, rpcServer string, keysFilePath stri
return err return err
} }
dagInfo, err := rpcClient.GetBlockDAGInfo()
if err != nil {
return nil
}
coinbaseMaturity := params.BlockCoinbaseMaturity
if dagInfo.NetworkName == "kaspa-testnet-11" {
coinbaseMaturity = 1000
}
serverInstance := &server{ serverInstance := &server{
rpcClient: rpcClient, rpcClient: rpcClient,
backgroundRPCClient: backgroundRPCClient,
params: params, params: params,
coinbaseMaturity: coinbaseMaturity,
utxosSortedByAmount: []*walletUTXO{}, utxosSortedByAmount: []*walletUTXO{},
nextSyncStartIndex: 0, nextSyncStartIndex: 0,
keysFile: keysFile, keysFile: keysFile,
shutdown: make(chan struct{}), shutdown: make(chan struct{}),
forceSyncChan: make(chan struct{}),
addressSet: make(walletAddressSet), addressSet: make(walletAddressSet),
txMassCalculator: txmass.NewCalculator(params.MassPerTxByte, params.MassPerScriptPubKeyByte, params.MassPerSigOp), txMassCalculator: txmass.NewCalculator(params.MassPerTxByte, params.MassPerScriptPubKeyByte, params.MassPerSigOp),
usedOutpoints: map[externalapi.DomainOutpoint]time.Time{}, usedOutpoints: map[externalapi.DomainOutpoint]time.Time{},
@ -98,8 +124,8 @@ func Start(params *dagconfig.Params, listen, rpcServer string, keysFilePath stri
} }
log.Infof("Read, syncing the wallet...") log.Infof("Read, syncing the wallet...")
spawn("serverInstance.sync", func() { spawn("serverInstance.syncLoop", func() {
err := serverInstance.sync() err := serverInstance.syncLoop()
if err != nil { if err != nil {
printErrorAndExit(errors.Wrap(err, "error syncing the wallet")) printErrorAndExit(errors.Wrap(err, "error syncing the wallet"))
} }

View File

@ -264,7 +264,7 @@ func (s *server) moreUTXOsForMergeTransaction(alreadySelectedUTXOs []*libkaspawa
if _, ok := alreadySelectedUTXOsMap[*utxo.Outpoint]; ok { if _, ok := alreadySelectedUTXOsMap[*utxo.Outpoint]; ok {
continue continue
} }
if !isUTXOSpendable(utxo, dagInfo.VirtualDAAScore, s.params.BlockCoinbaseMaturity) { if !s.isUTXOSpendable(utxo, dagInfo.VirtualDAAScore) {
continue continue
} }
additionalUTXOs = append(additionalUTXOs, &libkaspawallet.UTXO{ additionalUTXOs = append(additionalUTXOs, &libkaspawallet.UTXO{

View File

@ -23,7 +23,7 @@ func (was walletAddressSet) strings() []string {
return addresses return addresses
} }
func (s *server) sync() error { func (s *server) syncLoop() error {
ticker := time.NewTicker(time.Second) ticker := time.NewTicker(time.Second)
defer ticker.Stop() defer ticker.Stop()
@ -32,29 +32,39 @@ func (s *server) sync() error {
return err return err
} }
err = s.refreshExistingUTXOsWithLock() err = s.refreshUTXOs()
if err != nil { if err != nil {
return err return err
} }
for range ticker.C { s.firstSyncDone.Store(true)
err = s.collectFarAddresses() log.Infof("Wallet is synced and ready for operation")
if err != nil {
return err for {
select {
case <-ticker.C:
case <-s.forceSyncChan:
} }
err = s.collectRecentAddresses() err := s.sync()
if err != nil {
return err
}
err = s.refreshExistingUTXOsWithLock()
if err != nil { if err != nil {
return err return err
} }
} }
}
return nil func (s *server) sync() error {
err := s.collectFarAddresses()
if err != nil {
return err
}
err = s.collectRecentAddresses()
if err != nil {
return err
}
return s.refreshUTXOs()
} }
const ( const (
@ -158,7 +168,7 @@ func (s *server) collectAddresses(start, end uint32) error {
return err return err
} }
getBalancesByAddressesResponse, err := s.rpcClient.GetBalancesByAddresses(addressSet.strings()) getBalancesByAddressesResponse, err := s.backgroundRPCClient.GetBalancesByAddresses(addressSet.strings())
if err != nil { if err != nil {
return err return err
} }
@ -208,15 +218,17 @@ func (s *server) updateAddressesAndLastUsedIndexes(requestedAddressSet walletAdd
return s.keysFile.SetLastUsedInternalIndex(lastUsedInternalIndex) return s.keysFile.SetLastUsedInternalIndex(lastUsedInternalIndex)
} }
func (s *server) refreshExistingUTXOsWithLock() error { func (s *server) usedOutpointHasExpired(outpointBroadcastTime time.Time) bool {
s.lock.Lock() // If the node returns a UTXO we previously attempted to spend and enough time has passed, we assume
defer s.lock.Unlock() // that the network rejected or lost the previous transaction and allow a reuse. We set this time
// interval to a minute.
return s.refreshUTXOs() // We also verify that a full refresh UTXO operation started after this time point and has already
// completed, in order to make sure that indeed this state reflects a state obtained following the required wait time.
return s.startTimeOfLastCompletedRefresh.After(outpointBroadcastTime.Add(time.Minute))
} }
// updateUTXOSet clears the current UTXO set, and re-fills it with the given entries // updateUTXOSet clears the current UTXO set, and re-fills it with the given entries
func (s *server) updateUTXOSet(entries []*appmessage.UTXOsByAddressesEntry, mempoolEntries []*appmessage.MempoolEntryByAddress) error { func (s *server) updateUTXOSet(entries []*appmessage.UTXOsByAddressesEntry, mempoolEntries []*appmessage.MempoolEntryByAddress, refreshStart time.Time) error {
utxos := make([]*walletUTXO, 0, len(entries)) utxos := make([]*walletUTXO, 0, len(entries))
exclude := make(map[appmessage.RPCOutpoint]struct{}) exclude := make(map[appmessage.RPCOutpoint]struct{})
@ -243,6 +255,7 @@ func (s *server) updateUTXOSet(entries []*appmessage.UTXOsByAddressesEntry, memp
return err return err
} }
// No need to lock for reading since the only writer of this set is on `syncLoop` on the same goroutine.
address, ok := s.addressSet[entry.Address] address, ok := s.addressSet[entry.Address]
if !ok { if !ok {
return errors.Errorf("Got result from address %s even though it wasn't requested", entry.Address) return errors.Errorf("Got result from address %s even though it wasn't requested", entry.Address)
@ -256,32 +269,56 @@ func (s *server) updateUTXOSet(entries []*appmessage.UTXOsByAddressesEntry, memp
sort.Slice(utxos, func(i, j int) bool { return utxos[i].UTXOEntry.Amount() > utxos[j].UTXOEntry.Amount() }) sort.Slice(utxos, func(i, j int) bool { return utxos[i].UTXOEntry.Amount() > utxos[j].UTXOEntry.Amount() })
s.lock.Lock()
s.startTimeOfLastCompletedRefresh = refreshStart
s.utxosSortedByAmount = utxos s.utxosSortedByAmount = utxos
// Cleanup expired used outpoints to avoid a memory leak
for outpoint, broadcastTime := range s.usedOutpoints {
if s.usedOutpointHasExpired(broadcastTime) {
delete(s.usedOutpoints, outpoint)
}
}
s.lock.Unlock()
return nil return nil
} }
func (s *server) refreshUTXOs() error { func (s *server) refreshUTXOs() error {
refreshStart := time.Now()
// No need to lock for reading since the only writer of this set is on `syncLoop` on the same goroutine.
addresses := s.addressSet.strings()
// It's important to check the mempool before calling `GetUTXOsByAddresses`: // It's important to check the mempool before calling `GetUTXOsByAddresses`:
// If we would do it the other way around an output can be spent in the mempool // If we would do it the other way around an output can be spent in the mempool
// and not in consensus, and between the calls its spending transaction will be // and not in consensus, and between the calls its spending transaction will be
// added to consensus and removed from the mempool, so `getUTXOsByAddressesResponse` // added to consensus and removed from the mempool, so `getUTXOsByAddressesResponse`
// will include an obsolete output. // will include an obsolete output.
mempoolEntriesByAddresses, err := s.rpcClient.GetMempoolEntriesByAddresses(s.addressSet.strings(), true, true) mempoolEntriesByAddresses, err := s.backgroundRPCClient.GetMempoolEntriesByAddresses(addresses, true, true)
if err != nil { if err != nil {
return err return err
} }
getUTXOsByAddressesResponse, err := s.rpcClient.GetUTXOsByAddresses(s.addressSet.strings()) getUTXOsByAddressesResponse, err := s.backgroundRPCClient.GetUTXOsByAddresses(addresses)
if err != nil { if err != nil {
return err return err
} }
return s.updateUTXOSet(getUTXOsByAddressesResponse.Entries, mempoolEntriesByAddresses.Entries) return s.updateUTXOSet(getUTXOsByAddressesResponse.Entries, mempoolEntriesByAddresses.Entries, refreshStart)
}
func (s *server) forceSync() {
// Technically if two callers check the `if` simultaneously they will both spawn a
// goroutine, but we don't care about the small redundancy in such a rare case.
if len(s.forceSyncChan) == 0 {
go func() {
s.forceSyncChan <- struct{}{}
}()
}
} }
func (s *server) isSynced() bool { func (s *server) isSynced() bool {
return s.nextSyncStartIndex > s.maxUsedIndex() return s.nextSyncStartIndex > s.maxUsedIndex() && s.firstSyncDone.Load()
} }
func (s *server) formatSyncStateReport() string { func (s *server) formatSyncStateReport() string {
@ -291,8 +328,11 @@ func (s *server) formatSyncStateReport() string {
maxUsedIndex = s.nextSyncStartIndex maxUsedIndex = s.nextSyncStartIndex
} }
return fmt.Sprintf("scanned %d out of %d addresses (%.2f%%)", if s.nextSyncStartIndex < s.maxUsedIndex() {
s.nextSyncStartIndex, maxUsedIndex, float64(s.nextSyncStartIndex)*100.0/float64(maxUsedIndex)) return fmt.Sprintf("scanned %d out of %d addresses (%.2f%%)",
s.nextSyncStartIndex, maxUsedIndex, float64(s.nextSyncStartIndex)*100.0/float64(maxUsedIndex))
}
return "loading the wallet UTXO set"
} }
func (s *server) updateSyncingProgressLog(currProcessedAddresses, currMaxUsedAddresses uint32) { func (s *server) updateSyncingProgressLog(currProcessedAddresses, currMaxUsedAddresses uint32) {
@ -311,7 +351,7 @@ func (s *server) updateSyncingProgressLog(currProcessedAddresses, currMaxUsedAdd
if s.maxProcessedAddressesForLog >= s.maxUsedAddressesForLog { if s.maxProcessedAddressesForLog >= s.maxUsedAddressesForLog {
if !s.isLogFinalProgressLineShown { if !s.isLogFinalProgressLineShown {
log.Infof("Wallet is synced, ready for queries") log.Infof("Finished scanning recent addresses")
s.isLogFinalProgressLineShown = true s.isLogFinalProgressLineShown = true
} }
} else { } else {

View File

@ -0,0 +1,16 @@
package server
import (
"context"
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
"github.com/kaspanet/kaspad/version"
)
func (s *server) GetVersion(_ context.Context, _ *pb.GetVersionRequest) (*pb.GetVersionResponse, error) {
s.lock.RLock()
defer s.lock.RUnlock()
return &pb.GetVersionResponse{
Version: version.Version(),
}, nil
}

View File

@ -0,0 +1,26 @@
package main
import (
"context"
"fmt"
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/client"
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
)
func getDaemonVersion(conf *getDaemonVersionConfig) 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.GetVersion(ctx, &pb.GetVersionRequest{})
if err != nil {
return err
}
fmt.Println(response.Version)
return nil
}

View File

@ -1,10 +1,11 @@
package main package main
import "github.com/pkg/errors" import (
"github.com/pkg/errors"
)
func main() { func main() {
subCmd, config := parseCommandLine() subCmd, config := parseCommandLine()
var err error var err error
switch subCmd { switch subCmd {
case createSubCmd: case createSubCmd:
@ -31,6 +32,10 @@ func main() {
err = startDaemon(config.(*startDaemonConfig)) err = startDaemon(config.(*startDaemonConfig))
case sweepSubCmd: case sweepSubCmd:
err = sweep(config.(*sweepConfig)) err = sweep(config.(*sweepConfig))
case versionSubCmd:
showVersion()
case getDaemonVersionSubCmd:
err = getDaemonVersion(config.(*getDaemonVersionConfig))
default: default:
err = errors.Errorf("Unknown sub-command '%s'\n", subCmd) err = errors.Errorf("Unknown sub-command '%s'\n", subCmd)
} }

View File

@ -75,23 +75,29 @@ func send(conf *sendConfig) error {
signedTransactions[i] = signedTransaction signedTransactions[i] = signedTransaction
} }
if len(signedTransactions) > 1 { fmt.Printf("Broadcasting %d transaction(s)\n", len(signedTransactions))
fmt.Printf("Broadcasting %d transactions\n", len(signedTransactions))
}
// Since we waited for user input when getting the password, which could take unbound amount of time - // Since we waited for user input when getting the password, which could take unbound amount of time -
// create a new context for broadcast, to reset the timeout. // create a new context for broadcast, to reset the timeout.
broadcastCtx, broadcastCancel := context.WithTimeout(context.Background(), daemonTimeout) broadcastCtx, broadcastCancel := context.WithTimeout(context.Background(), daemonTimeout)
defer broadcastCancel() defer broadcastCancel()
response, err := daemonClient.Broadcast(broadcastCtx, &pb.BroadcastRequest{Transactions: signedTransactions}) const chunkSize = 100 // To avoid sending a message bigger than the gRPC max message size, we split it to chunks
if err != nil { for offset := 0; offset < len(signedTransactions); offset += chunkSize {
return err end := len(signedTransactions)
} if offset+chunkSize <= len(signedTransactions) {
fmt.Println("Transactions were sent successfully") end = offset + chunkSize
fmt.Println("Transaction ID(s): ") }
for _, txID := range response.TxIDs {
fmt.Printf("\t%s\n", txID) chunk := signedTransactions[offset:end]
response, err := daemonClient.Broadcast(broadcastCtx, &pb.BroadcastRequest{Transactions: chunk})
if err != nil {
return err
}
fmt.Printf("Broadcasted %d transaction(s) (broadcasted %.2f%% of the transactions so far)\n", len(chunk), 100*float64(end)/float64(len(signedTransactions)))
fmt.Println("Broadcasted Transaction ID(s): ")
for _, txID := range response.TxIDs {
fmt.Printf("\t%s\n", txID)
}
} }
if conf.Verbose { if conf.Verbose {

View File

@ -0,0 +1,15 @@
package main
import (
"fmt"
"github.com/kaspanet/kaspad/version"
"os"
"path/filepath"
"strings"
)
func showVersion() {
appName := filepath.Base(os.Args[0])
appName = strings.TrimSuffix(appName, filepath.Ext(appName))
fmt.Println(appName, "version", version.Version())
}

View File

@ -1,23 +1,44 @@
package rpcclient package rpcclient
import ( import (
"strings"
"github.com/kaspanet/kaspad/app/appmessage" "github.com/kaspanet/kaspad/app/appmessage"
) )
// SubmitTransaction sends an RPC request respective to the function's name and returns the RPC server's response // SubmitTransaction sends an RPC request respective to the function's name and returns the RPC server's response
func (c *RPCClient) SubmitTransaction(transaction *appmessage.RPCTransaction, allowOrphan bool) (*appmessage.SubmitTransactionResponseMessage, error) { func (c *RPCClient) SubmitTransaction(transaction *appmessage.RPCTransaction, transactionID string, allowOrphan bool) (*appmessage.SubmitTransactionResponseMessage, error) {
err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewSubmitTransactionRequestMessage(transaction, allowOrphan)) err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewSubmitTransactionRequestMessage(transaction, allowOrphan))
if err != nil { if err != nil {
return nil, err return nil, err
} }
response, err := c.route(appmessage.CmdSubmitTransactionResponseMessage).DequeueWithTimeout(c.timeout) for {
if err != nil { response, err := c.route(appmessage.CmdSubmitTransactionResponseMessage).DequeueWithTimeout(c.timeout)
return nil, err if err != nil {
} return nil, err
submitTransactionResponse := response.(*appmessage.SubmitTransactionResponseMessage) }
if submitTransactionResponse.Error != nil { submitTransactionResponse := response.(*appmessage.SubmitTransactionResponseMessage)
return nil, c.convertRPCError(submitTransactionResponse.Error) // Match the response to the expected ID. If they are different it means we got an old response which we
} // previously timed-out on, so we log and continue waiting for the correct current response.
if submitTransactionResponse.TransactionID != transactionID {
if submitTransactionResponse.Error != nil {
// A non-updated Kaspad might return an empty ID in the case of error, so in
// such a case we fallback to checking if the error contains the expected ID
if submitTransactionResponse.TransactionID != "" || !strings.Contains(submitTransactionResponse.Error.Message, transactionID) {
log.Warnf("SubmitTransaction: received an error response for previous request: %s", submitTransactionResponse.Error)
continue
}
return submitTransactionResponse, nil } else {
log.Warnf("SubmitTransaction: received a successful response for previous request with ID %s",
submitTransactionResponse.TransactionID)
continue
}
}
if submitTransactionResponse.Error != nil {
return nil, c.convertRPCError(submitTransactionResponse.Error)
}
return submitTransactionResponse, nil
}
} }

View File

@ -86,7 +86,7 @@ func submitAnAmountOfTransactionsToTheMempool(t *testing.T, rpcClient *rpcclient
for i, transaction := range transactions { for i, transaction := range transactions {
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction) rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
_, err := rpcClient.SubmitTransaction(rpcTransaction, false) _, err := rpcClient.SubmitTransaction(rpcTransaction, consensushashing.TransactionID(transaction).String(), false)
if err != nil { if err != nil {
if ignoreOrphanRejects && strings.Contains(err.Error(), "orphan") { if ignoreOrphanRejects && strings.Contains(err.Error(), "orphan") {
continue continue

View File

@ -53,7 +53,7 @@ func TestTxRelay(t *testing.T) {
msgTx := generateTx(t, secondBlock.Transactions[transactionhelper.CoinbaseTransactionIndex], payer, payee) msgTx := generateTx(t, secondBlock.Transactions[transactionhelper.CoinbaseTransactionIndex], payer, payee)
domainTransaction := appmessage.MsgTxToDomainTransaction(msgTx) domainTransaction := appmessage.MsgTxToDomainTransaction(msgTx)
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(domainTransaction) rpcTransaction := appmessage.DomainTransactionToRPCTransaction(domainTransaction)
response, err := payer.rpcClient.SubmitTransaction(rpcTransaction, false) response, err := payer.rpcClient.SubmitTransaction(rpcTransaction, consensushashing.TransactionID(domainTransaction).String(), false)
if err != nil { if err != nil {
t.Fatalf("Error submitting transaction: %+v", err) t.Fatalf("Error submitting transaction: %+v", err)
} }

View File

@ -88,8 +88,8 @@ func TestUTXOIndex(t *testing.T) {
// Submit a few transactions that spends some UTXOs // Submit a few transactions that spends some UTXOs
const transactionAmountToSpend = 5 const transactionAmountToSpend = 5
for i := 0; i < transactionAmountToSpend; i++ { for i := 0; i < transactionAmountToSpend; i++ {
rpcTransaction := buildTransactionForUTXOIndexTest(t, notificationEntries[i]) rpcTransaction, transactionID := buildTransactionForUTXOIndexTest(t, notificationEntries[i])
_, err = kaspad.rpcClient.SubmitTransaction(rpcTransaction, false) _, err = kaspad.rpcClient.SubmitTransaction(rpcTransaction, transactionID, false)
if err != nil { if err != nil {
t.Fatalf("Error submitting transaction: %s", err) t.Fatalf("Error submitting transaction: %s", err)
} }
@ -171,7 +171,7 @@ func TestUTXOIndex(t *testing.T) {
} }
} }
func buildTransactionForUTXOIndexTest(t *testing.T, entry *appmessage.UTXOsByAddressesEntry) *appmessage.RPCTransaction { func buildTransactionForUTXOIndexTest(t *testing.T, entry *appmessage.UTXOsByAddressesEntry) (*appmessage.RPCTransaction, string) {
transactionIDBytes, err := hex.DecodeString(entry.Outpoint.TransactionID) transactionIDBytes, err := hex.DecodeString(entry.Outpoint.TransactionID)
if err != nil { if err != nil {
t.Fatalf("Error decoding transaction ID: %s", err) t.Fatalf("Error decoding transaction ID: %s", err)
@ -224,5 +224,5 @@ func buildTransactionForUTXOIndexTest(t *testing.T, entry *appmessage.UTXOsByAdd
msgTx.TxIn[0].SignatureScript = signatureScript msgTx.TxIn[0].SignatureScript = signatureScript
domainTransaction := appmessage.MsgTxToDomainTransaction(msgTx) domainTransaction := appmessage.MsgTxToDomainTransaction(msgTx)
return appmessage.DomainTransactionToRPCTransaction(domainTransaction) return appmessage.DomainTransactionToRPCTransaction(domainTransaction), consensushashing.TransactionID(domainTransaction).String()
} }

View File

@ -11,7 +11,7 @@ const validCharacters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrs
const ( const (
appMajor uint = 0 appMajor uint = 0
appMinor uint = 12 appMinor uint = 12
appPatch uint = 15 appPatch uint = 17
) )
// appBuild is defined as a variable so it can be overridden during the build // appBuild is defined as a variable so it can be overridden during the build