Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kaspawallet daemon: Add Send and Sign commands #2016

Merged
merged 10 commits into from Apr 11, 2022
20 changes: 8 additions & 12 deletions cmd/kaspawallet/broadcast.go
Expand Up @@ -42,18 +42,14 @@ func broadcast(conf *broadcastConfig) error {
return err
}

transactionsCount := len(transactions)
for i, transaction := range transactions {
response, err := daemonClient.Broadcast(ctx, &pb.BroadcastRequest{Transaction: transaction})
if err != nil {
return err
}
if transactionsCount == 1 {
fmt.Println("Transaction was sent successfully")
} else {
fmt.Printf("Transaction %d (out of %d) was sent successfully\n", i+1, transactionsCount)
}
fmt.Printf("Transaction ID: \t%s\n", response.TxID)
response, err := daemonClient.Broadcast(ctx, &pb.BroadcastRequest{Transactions: transactions})
if err != nil {
return err
}
fmt.Println("Transaction was sent successfully")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Transactions were"

fmt.Println("Transaction ID(s): ")
for _, txID := range response.TxIDs {
fmt.Printf("\\t%s\\n", txID)
}

return nil
Expand Down
421 changes: 358 additions & 63 deletions cmd/kaspawallet/daemon/pb/kaspawalletd.pb.go

Large diffs are not rendered by default.

26 changes: 24 additions & 2 deletions cmd/kaspawallet/daemon/pb/kaspawalletd.proto
Expand Up @@ -10,6 +10,8 @@ service kaspawalletd {
rpc NewAddress (NewAddressRequest) returns (NewAddressResponse) {}
rpc Shutdown (ShutdownRequest) returns (ShutdownResponse) {}
rpc Broadcast (BroadcastRequest) returns (BroadcastResponse) {}
rpc Send(SendRequest) returns (SendResponse) {}
rpc Sign(SignRequest) returns (SignResponse) {}
}

message GetBalanceRequest {
Expand Down Expand Up @@ -51,15 +53,35 @@ message NewAddressResponse {
}

message BroadcastRequest {
bytes transaction = 1;
repeated bytes transactions = 1;
}

message BroadcastResponse {
string txID = 1;
repeated string txIDs = 1;
}

message ShutdownRequest {
}

message ShutdownResponse {
}

message SendRequest{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment that explains that this should be used only on trusted or secured connection?

string toAddress = 1;
uint64 amount = 2;

string password = 3;
}

message SendResponse{
repeated string txIDs = 1;
}

message SignRequest{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment that explains that this should be used only on trusted or secured connection?

repeated bytes unsignedTransactions = 1;
string password = 2;
}

message SignResponse{
repeated bytes signedTransactions = 1;
}
72 changes: 72 additions & 0 deletions cmd/kaspawallet/daemon/pb/kaspawalletd_grpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 18 additions & 5 deletions cmd/kaspawallet/daemon/server/broadcast.go
Expand Up @@ -12,17 +12,30 @@ import (
)

func (s *server) Broadcast(_ context.Context, request *pb.BroadcastRequest) (*pb.BroadcastResponse, error) {
tx, err := libkaspawallet.ExtractTransaction(request.Transaction, s.keysFile.ECDSA)
txIDs, err := s.broadcast(request.Transactions)
if err != nil {
return nil, err
}

txID, err := sendTransaction(s.rpcClient, tx)
if err != nil {
return nil, err
return &pb.BroadcastResponse{TxIDs: txIDs}, nil
}

func (s *server) broadcast(transactions [][]byte) ([]string, error) {
txIDs := make([]string, len(transactions))

for i, transaction := range transactions {
tx, err := libkaspawallet.ExtractTransaction(transaction, s.keysFile.ECDSA)
if err != nil {
return nil, err
}

txIDs[i], err = sendTransaction(s.rpcClient, tx)
if err != nil {
return nil, err
}
}

return &pb.BroadcastResponse{TxID: txID}, nil
return txIDs, nil
}

func sendTransaction(client *rpcclient.RPCClient, tx *externalapi.DomainTransaction) (string, error) {
Expand Down
18 changes: 13 additions & 5 deletions cmd/kaspawallet/daemon/server/create_unsigned_transaction.go
Expand Up @@ -18,6 +18,15 @@ func (s *server) CreateUnsignedTransactions(_ context.Context, request *pb.Creat
s.lock.Lock()
defer s.lock.Unlock()

unsignedTransactions, err := s.createUnsignedTransactions(request.Address, request.Amount)
if err != nil {
return nil, err
}

return &pb.CreateUnsignedTransactionsResponse{UnsignedTransactions: unsignedTransactions}, nil
}

func (s *server) createUnsignedTransactions(address string, amount uint64) ([][]byte, error) {
if !s.isSynced() {
return nil, errors.New("server is not synced")
}
Expand All @@ -27,12 +36,12 @@ func (s *server) CreateUnsignedTransactions(_ context.Context, request *pb.Creat
return nil, err
}

toAddress, err := util.DecodeAddress(request.Address, s.params.Prefix)
toAddress, err := util.DecodeAddress(address, s.params.Prefix)
if err != nil {
return nil, err
}

selectedUTXOs, changeSompi, err := s.selectUTXOs(request.Amount, feePerInput)
selectedUTXOs, changeSompi, err := s.selectUTXOs(amount, feePerInput)
if err != nil {
return nil, err
}
Expand All @@ -46,7 +55,7 @@ func (s *server) CreateUnsignedTransactions(_ context.Context, request *pb.Creat
s.keysFile.MinimumSignatures,
[]*libkaspawallet.Payment{{
Address: toAddress,
Amount: request.Amount,
Amount: amount,
}, {
Address: changeAddress,
Amount: changeSompi,
Expand All @@ -59,8 +68,7 @@ func (s *server) CreateUnsignedTransactions(_ context.Context, request *pb.Creat
if err != nil {
return nil, err
}

return &pb.CreateUnsignedTransactionsResponse{UnsignedTransactions: unsignedTransactions}, nil
return unsignedTransactions, nil
}

func (s *server) selectUTXOs(spendAmount uint64, feePerInput uint64) (
Expand Down
25 changes: 25 additions & 0 deletions cmd/kaspawallet/daemon/server/send.go
@@ -0,0 +1,25 @@
package server

import (
"context"

"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
)

func (s *server) Send(_ context.Context, request *pb.SendRequest) (*pb.SendResponse, error) {
unsignedTransactions, err := s.createUnsignedTransactions(request.ToAddress, request.Amount)
if err != nil {
return nil, err
}
signedTransactions, err := s.signTransactions(unsignedTransactions, request.Password)
if err != nil {
return nil, err
}

txIDs, err := s.broadcast(signedTransactions)
if err != nil {
return nil, err
}

return &pb.SendResponse{TxIDs: txIDs}, nil
}
36 changes: 36 additions & 0 deletions cmd/kaspawallet/daemon/server/sign.go
@@ -0,0 +1,36 @@
package server

import (
"context"

"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"

"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
)

func (s *server) Sign(_ context.Context, request *pb.SignRequest) (*pb.SignResponse, error) {
s.lock.Lock()
defer s.lock.Unlock()

signedTransactions, err := s.signTransactions(request.UnsignedTransactions, request.Password)
if err != nil {
return nil, err
}
return &pb.SignResponse{SignedTransactions: signedTransactions}, nil
}

func (s *server) signTransactions(unsignedTransactions [][]byte, password string) ([][]byte, error) {
mnemonics, err := s.keysFile.DecryptMnemonics(password)
if err != nil {
return nil, err
}
signedTransactions := make([][]byte, len(unsignedTransactions))
for i, unsignedTransaction := range unsignedTransactions {
signedTransaction, err := libkaspawallet.Sign(s.params, mnemonics, unsignedTransaction, s.keysFile.ECDSA)
if err != nil {
return nil, err
}
signedTransactions[i] = signedTransaction
}
return signedTransactions, nil
}
6 changes: 5 additions & 1 deletion cmd/kaspawallet/dump_unencrypted_data.go
Expand Up @@ -3,10 +3,11 @@ package main
import (
"bufio"
"fmt"
"os"

"github.com/kaspanet/kaspad/cmd/kaspawallet/keys"
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
"github.com/kaspanet/kaspad/cmd/kaspawallet/utils"
"os"

"github.com/pkg/errors"
)
Expand All @@ -24,6 +25,9 @@ func dumpUnencryptedData(conf *dumpUnencryptedDataConfig) error {
return err
}

if len(conf.Password) == 0 {
conf.Password = keys.GetPassword("Password:")
}
mnemonics, err := keysFile.DecryptMnemonics(conf.Password)
if err != nil {
return err
Expand Down
7 changes: 4 additions & 3 deletions cmd/kaspawallet/keys/create.go
Expand Up @@ -5,12 +5,13 @@ import (
"crypto/rand"
"crypto/subtle"
"fmt"
"os"

"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
"github.com/kaspanet/kaspad/cmd/kaspawallet/utils"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/pkg/errors"
"github.com/tyler-smith/go-bip39"
"os"
)

// CreateMnemonics generates `numKeys` number of mnemonics.
Expand Down Expand Up @@ -52,8 +53,8 @@ func encryptedMnemonicExtendedPublicKeyPairs(params *dagconfig.Params, mnemonics
password := []byte(cmdLinePassword)
if len(password) == 0 {

password = getPassword("Enter password for the key file:")
confirmPassword := getPassword("Confirm password:")
password = []byte(GetPassword("Enter password for the key file:"))
confirmPassword := []byte(GetPassword("Confirm password:"))

if subtle.ConstantTimeCompare(password, confirmPassword) != 1 {
return nil, nil, errors.New("Passwords are not identical")
Expand Down