diff --git a/cmd/kaspawallet/daemon/pb/kaspawalletd.pb.go b/cmd/kaspawallet/daemon/pb/kaspawalletd.pb.go index 5ee1f1fd7..1cd282757 100644 --- a/cmd/kaspawallet/daemon/pb/kaspawalletd.pb.go +++ b/cmd/kaspawallet/daemon/pb/kaspawalletd.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.0 +// protoc-gen-go v1.26.0 // protoc v3.17.2 // source: kaspawalletd.proto diff --git a/cmd/kaspawallet/daemon/server/create_unsigned_transaction.go b/cmd/kaspawallet/daemon/server/create_unsigned_transaction.go index dca075570..cbb316601 100644 --- a/cmd/kaspawallet/daemon/server/create_unsigned_transaction.go +++ b/cmd/kaspawallet/daemon/server/create_unsigned_transaction.go @@ -13,9 +13,6 @@ import ( "golang.org/x/exp/slices" ) -// TODO: Implement a better fee estimation mechanism -const feePerInput = 10000 - func (s *server) CreateUnsignedTransactions(_ context.Context, request *pb.CreateUnsignedTransactionsRequest) ( *pb.CreateUnsignedTransactionsResponse, error, ) { @@ -56,7 +53,7 @@ func (s *server) createUnsignedTransactions(address string, amount uint64, fromA fromAddresses = append(fromAddresses, fromAddress) } - selectedUTXOs, changeSompi, err := s.selectUTXOs(amount, feePerInput, fromAddresses) + selectedUTXOs, changeSompi, err := s.selectUTXOs(amount, libkaspawallet.FeePerInput, fromAddresses) if err != nil { return nil, err } diff --git a/cmd/kaspawallet/daemon/server/external_spendable_utxos.go b/cmd/kaspawallet/daemon/server/external_spendable_utxos.go index 15d94cd06..40d48db1b 100644 --- a/cmd/kaspawallet/daemon/server/external_spendable_utxos.go +++ b/cmd/kaspawallet/daemon/server/external_spendable_utxos.go @@ -55,7 +55,7 @@ func (s *server) selectExternalSpendableUTXOs(externalUTXOs *appmessage.GetUTXOs func isExternalUTXOSpendable(entry *appmessage.UTXOsByAddressesEntry, virtualDAAScore uint64, coinbaseMaturity uint64) bool { if !entry.UTXOEntry.IsCoinbase { return true - } else if entry.UTXOEntry.Amount <= feePerInput { + } else if entry.UTXOEntry.Amount <= libkaspawallet.FeePerInput { return false } return entry.UTXOEntry.BlockDAAScore+coinbaseMaturity < virtualDAAScore diff --git a/cmd/kaspawallet/daemon/server/split_transaction.go b/cmd/kaspawallet/daemon/server/split_transaction.go index b2154c900..00bf9e981 100644 --- a/cmd/kaspawallet/daemon/server/split_transaction.go +++ b/cmd/kaspawallet/daemon/server/split_transaction.go @@ -71,7 +71,7 @@ func (s *server) mergeTransaction( DerivationPath: s.walletAddressPath(changeWalletAddress), } totalValue += output.Value - totalValue -= feePerInput + totalValue -= libkaspawallet.FeePerInput } if totalValue < sentValue { @@ -206,7 +206,7 @@ func (s *server) createSplitTransaction(transaction *serialization.PartiallySign }) totalSompi += selectedUTXOs[i-startIndex].UTXOEntry.Amount() - totalSompi -= feePerInput + totalSompi -= libkaspawallet.FeePerInput } unsignedTransactionBytes, err := libkaspawallet.CreateUnsignedTransaction(s.keysFile.ExtendedPublicKeys, s.keysFile.MinimumSignatures, @@ -271,7 +271,7 @@ func (s *server) moreUTXOsForMergeTransaction(alreadySelectedUTXOs []*libkaspawa Outpoint: utxo.Outpoint, UTXOEntry: utxo.UTXOEntry, DerivationPath: s.walletAddressPath(utxo.address)}) - totalValueAdded += utxo.UTXOEntry.Amount() - feePerInput + totalValueAdded += utxo.UTXOEntry.Amount() - libkaspawallet.FeePerInput if totalValueAdded >= requiredAmount { break } diff --git a/cmd/kaspawallet/libkaspawallet/fees.go b/cmd/kaspawallet/libkaspawallet/fees.go new file mode 100644 index 000000000..fe24cba25 --- /dev/null +++ b/cmd/kaspawallet/libkaspawallet/fees.go @@ -0,0 +1,54 @@ +package libkaspawallet + +import ( + "github.com/pkg/errors" + + "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" +) + +// TODO: Implement a better fee construction and estimation mechanism. + +//FeePerInput is the current constant per input to pay for transactions. +const FeePerInput uint64 = 10000 + +//CalculateFees calculates the totalFee for a slice of transactions +func CalculateFees(transactions []*externalapi.DomainTransaction) (uint64, error) { + + var totalFee uint64 + + for _, tx := range transactions { + fee, err := CalculateFee(tx) + if err != nil { + return 0, err + } + totalFee += fee + } + + return totalFee, nil +} + +//CalculateFee calculates fee for a transaction +func CalculateFee(transaction *externalapi.DomainTransaction) (uint64, error) { + + var totalInputAmount uint64 + var totalOutputAmount uint64 + + for _, input := range transaction.Inputs { + totalInputAmount += input.UTXOEntry.Amount() + } + for _, output := range transaction.Outputs { + totalOutputAmount += output.Value + } + + return CalculateFeeFromInputAndOutputTotalAmounts(totalInputAmount, totalOutputAmount) +} + +//CalculateFeeFromInputAndOutputTotalAmounts calculates tx fee form total input and output amounts +//this func is more performant then CalculateFee if total input and output amounts are knowen. +func CalculateFeeFromInputAndOutputTotalAmounts(totalInputAmount uint64, totalOutputAmount uint64) (uint64, error) { + if totalInputAmount > totalOutputAmount { + return 0, errors.Errorf("The input amount may not exceed the output amount, Cannot Calculate negative fees") + } + + return totalInputAmount - totalOutputAmount, nil +} diff --git a/cmd/kaspawallet/parse.go b/cmd/kaspawallet/parse.go index 0e278e90f..e21b8a5eb 100644 --- a/cmd/kaspawallet/parse.go +++ b/cmd/kaspawallet/parse.go @@ -3,13 +3,15 @@ package main import ( "encoding/hex" "fmt" + "io/ioutil" + "strings" + + "github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet" "github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet/serialization" "github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing" "github.com/kaspanet/kaspad/domain/consensus/utils/constants" "github.com/kaspanet/kaspad/domain/consensus/utils/txscript" "github.com/pkg/errors" - "io/ioutil" - "strings" ) func parse(conf *parseConfig) error { @@ -42,7 +44,6 @@ func parse(conf *parseConfig) error { fmt.Printf("Transaction #%d ID: \t%s\n", i+1, consensushashing.TransactionID(partiallySignedTransaction.Tx)) fmt.Println() - allInputSompi := uint64(0) for index, input := range partiallySignedTransaction.Tx.Inputs { partiallySignedInput := partiallySignedTransaction.PartiallySignedInputs[index] @@ -51,7 +52,6 @@ func parse(conf *parseConfig) error { fmt.Printf("Input %d: \tOutpoint: %s:%d \tAmount: %.2f Kaspa\n", index, input.PreviousOutpoint.TransactionID, input.PreviousOutpoint.Index, float64(partiallySignedInput.PrevOutput.Value)/float64(constants.SompiPerKaspa)) } - allInputSompi += partiallySignedInput.PrevOutput.Value } if conf.Verbose { @@ -73,12 +73,18 @@ func parse(conf *parseConfig) error { fmt.Printf("Output %d: \tRecipient: %s \tAmount: %.2f Kaspa\n", index, addressString, float64(output.Value)/float64(constants.SompiPerKaspa)) - allOutputSompi += output.Value + } fmt.Println() - fmt.Printf("Fee:\t%d Sompi\n\n", allInputSompi-allOutputSompi) + fee, err := libkaspawallet.CalculateFeeFromInputAndOutputTotalAmounts(allInputSompi, allOutputSompi) + if err != nil { + return err + } + + fmt.Printf("Fee:\t%d Sompi\n\n", fee) + } return nil diff --git a/cmd/kaspawallet/sweep.go b/cmd/kaspawallet/sweep.go index d36065d05..33c53827e 100644 --- a/cmd/kaspawallet/sweep.go +++ b/cmd/kaspawallet/sweep.go @@ -24,8 +24,6 @@ import ( "github.com/pkg/errors" ) -const feePerInput = 10000 - func sweep(conf *sweepConfig) error { privateKeyBytes, err := hex.DecodeString(conf.PrivateKey) @@ -89,7 +87,7 @@ func sweep(conf *sweepConfig) error { return err } - splitTransactions, err := createSplitTransactionsWithSchnorrPrivteKey(conf.NetParams(), UTXOs, toAddress, feePerInput) + splitTransactions, err := createSplitTransactionsWithSchnorrPrivteKey(conf.NetParams(), UTXOs, toAddress) if err != nil { return err } @@ -141,8 +139,7 @@ func newDummyTransaction() *externalapi.DomainTransaction { func createSplitTransactionsWithSchnorrPrivteKey( params *dagconfig.Params, selectedUTXOs []*libkaspawallet.UTXO, - toAddress util.Address, - feePerInput int) ([]*externalapi.DomainTransaction, error) { + toAddress util.Address) ([]*externalapi.DomainTransaction, error) { var splitTransactions []*externalapi.DomainTransaction @@ -180,7 +177,7 @@ func createSplitTransactionsWithSchnorrPrivteKey( ) currentTx.Outputs[0] = &externalapi.DomainTransactionOutput{ - Value: totalSplitAmount - uint64(len(currentTx.Inputs)*feePerInput), + Value: totalSplitAmount - uint64(len(currentTx.Inputs)*int(libkaspawallet.FeePerInput)), ScriptPublicKey: scriptPublicKey, }