Compare commits

...

18 Commits

Author SHA1 Message Date
Ori Newman
387fbf0451 Add comment 2023-12-27 14:17:31 +02:00
Ori Newman
265d76a393 small fixes 2023-12-27 14:15:40 +02:00
Ori Newman
057cc85c8c Compare wallet addresses by value 2023-12-27 14:02:41 +02:00
Michael Sutton
0534413213 one more rename 2023-12-27 00:03:25 +02:00
Michael Sutton
0428502c44 rename to follow conventions 2023-12-26 23:51:09 +02:00
Michael Sutton
c3ef43795b add an rpc client dedicated for background ops 2023-12-26 23:24:48 +02:00
Michael Sutton
9e3b89c357 fix small memory leak 2023-12-26 20:57:03 +02:00
Michael Sutton
f62a174985 lock address reading 2023-12-26 20:40:45 +02:00
Michael Sutton
0d66fa8cd8 fix expire condition 2023-12-26 20:33:05 +02:00
Ori Newman
eabbdad738 Better policy for used outpoints and wait for first sync when creating an unsigned tx 2023-12-26 19:03:14 +02:00
Ori Newman
c28eaa6998 Don't push to forceSyncChan if it has an element 2023-12-26 18:10:52 +02:00
Ori Newman
ee2a848412 Make a more granular lock for refreshUTXOs 2023-12-26 17:52:56 +02:00
Ori Newman
524a654886 Lazy wallet utxo sync after broadcasting a tx 2023-12-26 17:30:43 +02:00
Ori Newman
629faa8436 Add options to see wallet and wallet daemon versions (#2257) 2023-12-26 16:00:44 +02:00
Michael Sutton
91e6c6b74b version bump + changelog (#2255) 2023-12-25 10:22:29 +02:00
Michael Sutton
0819244ba1 if the tx has change and thus so 2 outputs, try having at least 2 inputs as well (in order to not be slowed down by dust patch) (#2254) 2023-12-25 09:19:04 +02:00
Ori Newman
a0149cd8d0 Broadcast wallet transactions in chunks (#2253) 2023-12-13 16:15:25 +02:00
Ori Newman
5a3b8a0066 Fix type detection in RemoveInvalidTransactions (#2252) 2023-12-12 17:13:27 +02:00
21 changed files with 511 additions and 173 deletions

View File

@@ -1,3 +1,25 @@
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
===========================

View File

@@ -1,10 +1,9 @@
package main
import (
"os"
"github.com/kaspanet/kaspad/infrastructure/config"
"github.com/pkg/errors"
"os"
"github.com/jessevdk/go-flags"
)
@@ -22,6 +21,8 @@ const (
newAddressSubCmd = "new-address"
dumpUnencryptedDataSubCmd = "dump-unencrypted-data"
startDaemonSubCmd = "start-daemon"
versionSubCmd = "version"
getDaemonVersionSubCmd = "get-daemon-version"
)
const (
@@ -30,6 +31,7 @@ const (
)
type configFlags struct {
ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"`
config.NetworkFlags
}
@@ -129,6 +131,13 @@ type dumpUnencryptedDataConfig struct {
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{}) {
cfg := &configFlags{}
parser := flags.NewParser(cfg, flags.PrintErrors|flags.HelpFlag)
@@ -185,6 +194,9 @@ func parseCommandLine() (subCommand string, config interface{}) {
Listen: defaultListen,
}
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()
if err != nil {
@@ -290,6 +302,9 @@ func parseCommandLine() (subCommand string, config interface{}) {
printErrorAndExit(err)
}
config = startDaemonConf
case versionSubCmd:
case getDaemonVersionSubCmd:
config = getDaemonVersionConf
}
return parser.Command.Active.Name, config

View File

@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.21.12
// protoc-gen-go v1.27.1
// protoc v3.12.3
// source: kaspawalletd.proto
package pb
@@ -1242,6 +1242,91 @@ func (x *SignResponse) GetSignedTransactions() [][]byte {
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_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,
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,
0x69, 0x6f, 0x6e, 0x73, 0x32, 0xb3, 0x06, 0x0a, 0x0c, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61,
0x6c, 0x6c, 0x65, 0x74, 0x64, 0x12, 0x51, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61,
0x6e, 0x63, 0x65, 0x12, 0x1f, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65,
0x74, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c,
0x65, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7e, 0x0a, 0x19, 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, 0x12, 0x2e, 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,
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,
0x69, 0x6f, 0x6e, 0x73, 0x22, 0x13, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x2e, 0x0a, 0x12, 0x47, 0x65, 0x74,
0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x32, 0x86, 0x07, 0x0a, 0x0c, 0x6b, 0x61,
0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x12, 0x51, 0x0a, 0x0a, 0x47, 0x65,
0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1f, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61,
0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e,
0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6b, 0x61, 0x73, 0x70,
0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61,
0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7e, 0x0a,
0x19, 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, 0x12, 0x2e, 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, 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, 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, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x0d,
0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x22, 0x2e,
0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x68, 0x6f,
0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x23, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64,
0x2e, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x0a, 0x4e, 0x65, 0x77, 0x41,
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61,
0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77,
0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x08, 0x53,
0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x1d, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77,
0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61,
0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x09, 0x42, 0x72, 0x6f, 0x61,
0x64, 0x63, 0x61, 0x73, 0x74, 0x12, 0x1e, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c,
0x6c, 0x65, 0x74, 0x64, 0x2e, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c,
0x6c, 0x65, 0x74, 0x64, 0x2e, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x04, 0x53, 0x65, 0x6e, 0x64,
0x12, 0x19, 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, 0x1a, 0x1a, 0x2e, 0x6b, 0x61,
0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x04, 0x53, 0x69, 0x67,
0x6e, 0x12, 0x19, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64,
0x2e, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6b,
0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x69, 0x67, 0x6e,
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,
0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x00, 0x12, 0x5a, 0x0a, 0x0d, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
0x65, 0x73, 0x12, 0x22, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74,
0x64, 0x2e, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61,
0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a,
0x0a, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x2e, 0x6b, 0x61,
0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64,
0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6b,
0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x4e, 0x65, 0x77, 0x41,
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
0x12, 0x4b, 0x0a, 0x08, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x1d, 0x2e, 0x6b,
0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x68, 0x75, 0x74,
0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6b, 0x61,
0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64,
0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a,
0x09, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x12, 0x1e, 0x2e, 0x6b, 0x61, 0x73,
0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63,
0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6b, 0x61, 0x73,
0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63,
0x61, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a,
0x04, 0x53, 0x65, 0x6e, 0x64, 0x12, 0x19, 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,
0x1a, 0x1a, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e,
0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3f,
0x0a, 0x04, 0x53, 0x69, 0x67, 0x6e, 0x12, 0x19, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61,
0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x1a, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64,
0x2e, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
0x51, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e,
0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74,
0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20,
0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65,
0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 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 (
@@ -1441,7 +1536,7 @@ func file_kaspawalletd_proto_rawDescGZIP() []byte {
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{}{
(*GetBalanceRequest)(nil), // 0: kaspawalletd.GetBalanceRequest
(*GetBalanceResponse)(nil), // 1: kaspawalletd.GetBalanceResponse
@@ -1466,6 +1561,8 @@ var file_kaspawalletd_proto_goTypes = []interface{}{
(*SendResponse)(nil), // 20: kaspawalletd.SendResponse
(*SignRequest)(nil), // 21: kaspawalletd.SignRequest
(*SignResponse)(nil), // 22: kaspawalletd.SignResponse
(*GetVersionRequest)(nil), // 23: kaspawalletd.GetVersionRequest
(*GetVersionResponse)(nil), // 24: kaspawalletd.GetVersionResponse
}
var file_kaspawalletd_proto_depIdxs = []int32{
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
19, // 12: kaspawalletd.kaspawalletd.Send:input_type -> kaspawalletd.SendRequest
21, // 13: kaspawalletd.kaspawalletd.Sign:input_type -> kaspawalletd.SignRequest
1, // 14: kaspawalletd.kaspawalletd.GetBalance:output_type -> kaspawalletd.GetBalanceResponse
18, // 15: kaspawalletd.kaspawalletd.GetExternalSpendableUTXOs:output_type -> kaspawalletd.GetExternalSpendableUTXOsResponse
4, // 16: kaspawalletd.kaspawalletd.CreateUnsignedTransactions:output_type -> kaspawalletd.CreateUnsignedTransactionsResponse
6, // 17: kaspawalletd.kaspawalletd.ShowAddresses:output_type -> kaspawalletd.ShowAddressesResponse
8, // 18: kaspawalletd.kaspawalletd.NewAddress:output_type -> kaspawalletd.NewAddressResponse
12, // 19: kaspawalletd.kaspawalletd.Shutdown:output_type -> kaspawalletd.ShutdownResponse
10, // 20: kaspawalletd.kaspawalletd.Broadcast:output_type -> kaspawalletd.BroadcastResponse
20, // 21: kaspawalletd.kaspawalletd.Send:output_type -> kaspawalletd.SendResponse
22, // 22: kaspawalletd.kaspawalletd.Sign:output_type -> kaspawalletd.SignResponse
14, // [14:23] is the sub-list for method output_type
5, // [5:14] is the sub-list for method input_type
23, // 14: kaspawalletd.kaspawalletd.GetVersion:input_type -> kaspawalletd.GetVersionRequest
1, // 15: kaspawalletd.kaspawalletd.GetBalance:output_type -> kaspawalletd.GetBalanceResponse
18, // 16: kaspawalletd.kaspawalletd.GetExternalSpendableUTXOs:output_type -> kaspawalletd.GetExternalSpendableUTXOsResponse
4, // 17: kaspawalletd.kaspawalletd.CreateUnsignedTransactions:output_type -> kaspawalletd.CreateUnsignedTransactionsResponse
6, // 18: kaspawalletd.kaspawalletd.ShowAddresses:output_type -> kaspawalletd.ShowAddressesResponse
8, // 19: kaspawalletd.kaspawalletd.NewAddress:output_type -> kaspawalletd.NewAddressResponse
12, // 20: kaspawalletd.kaspawalletd.Shutdown:output_type -> kaspawalletd.ShutdownResponse
10, // 21: kaspawalletd.kaspawalletd.Broadcast:output_type -> kaspawalletd.BroadcastResponse
20, // 22: kaspawalletd.kaspawalletd.Send:output_type -> kaspawalletd.SendResponse
22, // 23: kaspawalletd.kaspawalletd.Sign:output_type -> kaspawalletd.SignResponse
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 extendee
0, // [0:5] is the sub-list for field type_name
@@ -1780,6 +1879,30 @@ func file_kaspawalletd_proto_init() {
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{}
out := protoimpl.TypeBuilder{
@@ -1787,7 +1910,7 @@ func file_kaspawalletd_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_kaspawalletd_proto_rawDesc,
NumEnums: 0,
NumMessages: 23,
NumMessages: 25,
NumExtensions: 0,
NumServices: 1,
},

View File

@@ -15,6 +15,7 @@ service kaspawalletd {
rpc Send(SendRequest) returns (SendResponse) {}
// Since SignRequest contains a password - this command should only be used on a trusted or secure connection
rpc Sign(SignRequest) returns (SignResponse) {}
rpc GetVersion(GetVersionRequest) returns (GetVersionResponse) {}
}
message GetBalanceRequest {
@@ -127,3 +128,10 @@ message SignRequest{
message SignResponse{
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.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.12.3
// source: kaspawalletd.proto
package pb
@@ -29,6 +33,7 @@ type KaspawalletdClient interface {
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
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 {
@@ -120,6 +125,15 @@ func (c *kaspawalletdClient) Sign(ctx context.Context, in *SignRequest, opts ...
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.
// All implementations must embed UnimplementedKaspawalletdServer
// for forward compatibility
@@ -135,6 +149,7 @@ type KaspawalletdServer interface {
Send(context.Context, *SendRequest) (*SendResponse, error)
// Since SignRequest contains a password - this command should only be used on a trusted or secure connection
Sign(context.Context, *SignRequest) (*SignResponse, error)
GetVersion(context.Context, *GetVersionRequest) (*GetVersionResponse, error)
mustEmbedUnimplementedKaspawalletdServer()
}
@@ -169,6 +184,9 @@ func (UnimplementedKaspawalletdServer) Send(context.Context, *SendRequest) (*Sen
func (UnimplementedKaspawalletdServer) Sign(context.Context, *SignRequest) (*SignResponse, error) {
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() {}
// 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)
}
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.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@@ -387,6 +423,10 @@ var Kaspawalletd_ServiceDesc = grpc.ServiceDesc{
MethodName: "Sign",
Handler: _Kaspawalletd_Sign_Handler,
},
{
MethodName: "GetVersion",
Handler: _Kaspawalletd_GetVersion_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "kaspawalletd.proto",

View File

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

View File

@@ -54,11 +54,7 @@ func (s *server) broadcast(transactions [][]byte, isDomain bool) ([]string, erro
}
}
err = s.refreshUTXOs()
if err != nil {
return nil, err
}
s.forceSync()
return txIDs, nil
}

View File

@@ -3,14 +3,12 @@ package server
import (
"context"
"fmt"
"time"
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"github.com/kaspanet/kaspad/util"
"github.com/pkg/errors"
"golang.org/x/exp/slices"
)
// TODO: Implement a better fee estimation mechanism
@@ -35,7 +33,6 @@ func (s *server) createUnsignedTransactions(address string, amount uint64, isSen
if !s.isSynced() {
return nil, errors.Errorf("wallet daemon is not synced yet, %s", s.formatSyncStateReport())
}
// make sure address string is correct before proceeding to a
// potentially long UTXO refreshment operation
toAddress, err := util.DecodeAddress(address, s.params.Prefix)
@@ -43,16 +40,11 @@ func (s *server) createUnsignedTransactions(address string, amount uint64, isSen
return nil, err
}
err = s.refreshUTXOs()
if err != nil {
return nil, err
}
var fromAddresses []*walletAddress
for _, from := range fromAddressesString {
fromAddress, exists := s.addressSet[from]
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)
}
@@ -106,19 +98,14 @@ func (s *server) selectUTXOs(spendAmount uint64, isSendAll bool, feePerInput uin
return nil, 0, 0, err
}
coinbaseMaturity := s.params.BlockCoinbaseMaturity
if dagInfo.NetworkName == "kaspa-testnet-11" {
coinbaseMaturity = 1000
}
for _, utxo := range s.utxosSortedByAmount {
if (fromAddresses != nil && !slices.Contains(fromAddresses, utxo.address)) ||
!isUTXOSpendable(utxo, dagInfo.VirtualDAAScore, coinbaseMaturity) {
if (fromAddresses != nil && !walletAddressesContain(fromAddresses, utxo.address)) ||
!s.isUTXOSpendable(utxo, dagInfo.VirtualDAAScore) {
continue
}
if broadcastTime, ok := s.usedOutpoints[*utxo.Outpoint]; ok {
if time.Since(broadcastTime) > time.Minute {
if s.usedOutpointHasExpired(broadcastTime) {
delete(s.usedOutpoints, *utxo.Outpoint)
} else {
continue
@@ -135,7 +122,11 @@ func (s *server) selectUTXOs(spendAmount uint64, isSendAll bool, feePerInput uin
fee := feePerInput * uint64(len(selectedUTXOs))
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 new dust
// rules we try and find at least 2 inputs (even though the next one is not necessary in terms of spend value)
if !isSendAll && (totalValue == totalSpend || (totalValue > totalSpend && len(selectedUTXOs) > 1)) {
break
}
}
@@ -156,3 +147,13 @@ func (s *server) selectUTXOs(spendAmount uint64, isSendAll bool, feePerInput uin
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"
"os"
"sync"
"sync/atomic"
"time"
"github.com/kaspanet/kaspad/version"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/util/txmass"
@@ -27,17 +30,22 @@ import (
type server struct {
pb.UnimplementedKaspawalletdServer
rpcClient *rpcclient.RPCClient
params *dagconfig.Params
rpcClient *rpcclient.RPCClient // RPC client for ongoing user requests
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
utxosSortedByAmount []*walletUTXO
nextSyncStartIndex uint32
keysFile *keys.File
shutdown chan struct{}
addressSet walletAddressSet
txMassCalculator *txmass.Calculator
usedOutpoints map[externalapi.DomainOutpoint]time.Time
lock sync.RWMutex
utxosSortedByAmount []*walletUTXO
nextSyncStartIndex uint32
keysFile *keys.File
shutdown chan struct{}
forceSyncChan chan struct{}
startTimeOfLastCompletedRefresh time.Time
addressSet walletAddressSet
txMassCalculator *txmass.Calculator
usedOutpoints map[externalapi.DomainOutpoint]time.Time
firstSyncDone atomic.Bool
isLogFinalProgressLineShown bool
maxUsedAddressesForLog uint32
@@ -59,6 +67,7 @@ func Start(params *dagconfig.Params, listen, rpcServer string, keysFilePath stri
profiling.Start(profile, log)
}
log.Infof("Version %s", version.Version())
listener, err := net.Listen("tcp", listen)
if err != nil {
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 {
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)
keysFile, err := keys.ReadKeysFile(params, keysFilePath)
@@ -82,13 +95,26 @@ func Start(params *dagconfig.Params, listen, rpcServer string, keysFilePath stri
return err
}
dagInfo, err := rpcClient.GetBlockDAGInfo()
if err != nil {
return nil
}
coinbaseMaturity := params.BlockCoinbaseMaturity
if dagInfo.NetworkName == "kaspa-testnet-11" {
coinbaseMaturity = 1000
}
serverInstance := &server{
rpcClient: rpcClient,
backgroundRPCClient: backgroundRPCClient,
params: params,
coinbaseMaturity: coinbaseMaturity,
utxosSortedByAmount: []*walletUTXO{},
nextSyncStartIndex: 0,
keysFile: keysFile,
shutdown: make(chan struct{}),
forceSyncChan: make(chan struct{}),
addressSet: make(walletAddressSet),
txMassCalculator: txmass.NewCalculator(params.MassPerTxByte, params.MassPerScriptPubKeyByte, params.MassPerSigOp),
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...")
spawn("serverInstance.sync", func() {
err := serverInstance.sync()
spawn("serverInstance.syncLoop", func() {
err := serverInstance.syncLoop()
if err != nil {
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 {
continue
}
if !isUTXOSpendable(utxo, dagInfo.VirtualDAAScore, s.params.BlockCoinbaseMaturity) {
if !s.isUTXOSpendable(utxo, dagInfo.VirtualDAAScore) {
continue
}
additionalUTXOs = append(additionalUTXOs, &libkaspawallet.UTXO{

View File

@@ -23,7 +23,7 @@ func (was walletAddressSet) strings() []string {
return addresses
}
func (s *server) sync() error {
func (s *server) syncLoop() error {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
@@ -32,29 +32,39 @@ func (s *server) sync() error {
return err
}
err = s.refreshExistingUTXOsWithLock()
err = s.refreshUTXOs()
if err != nil {
return err
}
for range ticker.C {
err = s.collectFarAddresses()
if err != nil {
return err
s.firstSyncDone.Store(true)
log.Infof("Wallet is synced and ready for operation")
for {
select {
case <-ticker.C:
case <-s.forceSyncChan:
}
err = s.collectRecentAddresses()
if err != nil {
return err
}
err = s.refreshExistingUTXOsWithLock()
err := s.sync()
if err != nil {
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 (
@@ -158,7 +168,7 @@ func (s *server) collectAddresses(start, end uint32) error {
return err
}
getBalancesByAddressesResponse, err := s.rpcClient.GetBalancesByAddresses(addressSet.strings())
getBalancesByAddressesResponse, err := s.backgroundRPCClient.GetBalancesByAddresses(addressSet.strings())
if err != nil {
return err
}
@@ -208,15 +218,17 @@ func (s *server) updateAddressesAndLastUsedIndexes(requestedAddressSet walletAdd
return s.keysFile.SetLastUsedInternalIndex(lastUsedInternalIndex)
}
func (s *server) refreshExistingUTXOsWithLock() error {
s.lock.Lock()
defer s.lock.Unlock()
return s.refreshUTXOs()
func (s *server) usedOutpointHasExpired(outpointBroadcastTime time.Time) bool {
// If the node returns a UTXO we previously attempted to spend and enough time has passed, we assume
// that the network rejected or lost the previous transaction and allow a reuse. We set this time
// interval to a minute.
// 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
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))
exclude := make(map[appmessage.RPCOutpoint]struct{})
@@ -243,6 +255,7 @@ func (s *server) updateUTXOSet(entries []*appmessage.UTXOsByAddressesEntry, memp
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]
if !ok {
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() })
s.lock.Lock()
s.startTimeOfLastCompletedRefresh = refreshStart
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
}
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`:
// 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
// added to consensus and removed from the mempool, so `getUTXOsByAddressesResponse`
// 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 {
return err
}
getUTXOsByAddressesResponse, err := s.rpcClient.GetUTXOsByAddresses(s.addressSet.strings())
getUTXOsByAddressesResponse, err := s.backgroundRPCClient.GetUTXOsByAddresses(addresses)
if err != nil {
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 {
return s.nextSyncStartIndex > s.maxUsedIndex()
return s.nextSyncStartIndex > s.maxUsedIndex() && s.firstSyncDone.Load()
}
func (s *server) formatSyncStateReport() string {
@@ -291,8 +328,11 @@ func (s *server) formatSyncStateReport() string {
maxUsedIndex = s.nextSyncStartIndex
}
return fmt.Sprintf("scanned %d out of %d addresses (%.2f%%)",
s.nextSyncStartIndex, maxUsedIndex, float64(s.nextSyncStartIndex)*100.0/float64(maxUsedIndex))
if s.nextSyncStartIndex < s.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) {
@@ -311,7 +351,7 @@ func (s *server) updateSyncingProgressLog(currProcessedAddresses, currMaxUsedAdd
if s.maxProcessedAddressesForLog >= s.maxUsedAddressesForLog {
if !s.isLogFinalProgressLineShown {
log.Infof("Wallet is synced, ready for queries")
log.Infof("Finished scanning recent addresses")
s.isLogFinalProgressLineShown = true
}
} 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
import "github.com/pkg/errors"
import (
"github.com/pkg/errors"
)
func main() {
subCmd, config := parseCommandLine()
var err error
switch subCmd {
case createSubCmd:
@@ -31,6 +32,10 @@ func main() {
err = startDaemon(config.(*startDaemonConfig))
case sweepSubCmd:
err = sweep(config.(*sweepConfig))
case versionSubCmd:
showVersion()
case getDaemonVersionSubCmd:
err = getDaemonVersion(config.(*getDaemonVersionConfig))
default:
err = errors.Errorf("Unknown sub-command '%s'\n", subCmd)
}

View File

@@ -75,23 +75,29 @@ func send(conf *sendConfig) error {
signedTransactions[i] = signedTransaction
}
if len(signedTransactions) > 1 {
fmt.Printf("Broadcasting %d transactions\n", len(signedTransactions))
}
fmt.Printf("Broadcasting %d transaction(s)\n", len(signedTransactions))
// 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.
broadcastCtx, broadcastCancel := context.WithTimeout(context.Background(), daemonTimeout)
defer broadcastCancel()
response, err := daemonClient.Broadcast(broadcastCtx, &pb.BroadcastRequest{Transactions: signedTransactions})
if err != nil {
return err
}
fmt.Println("Transactions were sent successfully")
fmt.Println("Transaction ID(s): ")
for _, txID := range response.TxIDs {
fmt.Printf("\t%s\n", txID)
const chunkSize = 100 // To avoid sending a message bigger than the gRPC max message size, we split it to chunks
for offset := 0; offset < len(signedTransactions); offset += chunkSize {
end := len(signedTransactions)
if offset+chunkSize <= len(signedTransactions) {
end = offset + chunkSize
}
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 {

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

@@ -131,11 +131,12 @@ func (bb *blockBuilder) validateTransactions(stagingArea *model.StagingArea,
for _, transaction := range transactions {
err := bb.validateTransaction(stagingArea, transaction)
if err != nil {
if !errors.As(err, &ruleerrors.RuleError{}) {
ruleError := ruleerrors.RuleError{}
if !errors.As(err, &ruleError) {
return err
}
invalidTransactions = append(invalidTransactions,
ruleerrors.InvalidTransaction{Transaction: transaction, Error: err})
ruleerrors.InvalidTransaction{Transaction: transaction, Error: &ruleError})
}
}

View File

@@ -315,7 +315,7 @@ func NewErrMissingParents(missingParentHashes []*externalapi.DomainHash) error {
// InvalidTransaction is a struct containing an invalid transaction, and the error explaining why it's invalid.
type InvalidTransaction struct {
Transaction *externalapi.DomainTransaction
Error error
Error *RuleError
}
func (invalid InvalidTransaction) String() string {

View File

@@ -3,9 +3,10 @@ package ruleerrors
import (
"errors"
"fmt"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"testing"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
)
@@ -49,7 +50,7 @@ func TestNewErrMissingTxOut(t *testing.T) {
func TestNewErrInvalidTransactionsInNewBlock(t *testing.T) {
tx := &externalapi.DomainTransaction{Fee: 1337}
txID := consensushashing.TransactionID(tx)
outer := NewErrInvalidTransactionsInNewBlock([]InvalidTransaction{{tx, ErrNoTxInputs}})
outer := NewErrInvalidTransactionsInNewBlock([]InvalidTransaction{{tx, &ErrNoTxInputs}})
//TODO: Implement Stringer for `DomainTransaction`
expectedOuterErr := fmt.Sprintf("ErrInvalidTransactionsInNewBlock: [(%s: ErrNoTxInputs)]", txID)
inner := &ErrInvalidTransactionsInNewBlock{}
@@ -60,7 +61,7 @@ func TestNewErrInvalidTransactionsInNewBlock(t *testing.T) {
if len(inner.InvalidTransactions) != 1 {
t.Fatalf("TestNewErrInvalidTransactionsInNewBlock: Expected len(inner.MissingOutpoints) 1, found: %d", len(inner.InvalidTransactions))
}
if inner.InvalidTransactions[0].Error != ErrNoTxInputs {
if *inner.InvalidTransactions[0].Error != ErrNoTxInputs {
t.Fatalf("TestNewErrInvalidTransactionsInNewBlock: Expected ErrNoTxInputs. found: %v", inner.InvalidTransactions[0].Error)
}
if inner.InvalidTransactions[0].Transaction.Fee != 1337 {

View File

@@ -1,10 +1,12 @@
package mempool
import (
"sync"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"sync"
"github.com/pkg/errors"
"github.com/kaspanet/kaspad/domain/consensusreference"
@@ -209,17 +211,7 @@ func (mp *mempool) RemoveInvalidTransactions(err *ruleerrors.ErrInvalidTransacti
defer mp.mtx.Unlock()
for _, tx := range err.InvalidTransactions {
ruleErr, success := tx.Error.(ruleerrors.RuleError)
if !success {
continue
}
inner := ruleErr.Unwrap()
removeRedeemers := true
if _, ok := inner.(ruleerrors.ErrMissingTxOut); ok {
removeRedeemers = false
}
removeRedeemers := !errors.As(tx.Error, &ruleerrors.ErrMissingTxOut{})
err := mp.removeTransaction(consensushashing.TransactionID(tx.Transaction), removeRedeemers)
if err != nil {
return err

View File

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