mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00
apply max fee constrains
This commit is contained in:
parent
0beb9edf12
commit
c0415eaaec
@ -61,7 +61,7 @@ func (s *server) BumpFee(_ context.Context, request *pb.BumpFeeRequest) (*pb.Bum
|
||||
|
||||
mass := s.txMassCalculator.CalculateTransactionOverallMass(domainTx)
|
||||
feeRate := float64(entry.Entry.Fee) / float64(mass)
|
||||
newFeeRate, err := s.calculateFeeRate(request.FeePolicy)
|
||||
newFeeRate, maxFee, err := s.calculateFeeLimits(request.FeePolicy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -87,7 +87,7 @@ func (s *server) BumpFee(_ context.Context, request *pb.BumpFeeRequest) (*pb.Bum
|
||||
for outpoint := range outpointsToInputs {
|
||||
allowUsed[outpoint] = struct{}{}
|
||||
}
|
||||
selectedUTXOs, spendValue, changeSompi, err := s.selectUTXOsWithPreselected([]*walletUTXO{maxUTXO}, allowUsed, domainTx.Outputs[0].Value, false, newFeeRate, fromAddresses)
|
||||
selectedUTXOs, spendValue, changeSompi, err := s.selectUTXOsWithPreselected([]*walletUTXO{maxUTXO}, allowUsed, domainTx.Outputs[0].Value, false, newFeeRate, maxFee, fromAddresses)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -128,7 +128,7 @@ func (s *server) BumpFee(_ context.Context, request *pb.BumpFeeRequest) (*pb.Bum
|
||||
return nil, err
|
||||
}
|
||||
|
||||
unsignedTransactions, err := s.maybeAutoCompoundTransaction(unsignedTransaction, toAddress, changeAddress, changeWalletAddress, newFeeRate)
|
||||
unsignedTransactions, err := s.maybeAutoCompoundTransaction(unsignedTransaction, toAddress, changeAddress, changeWalletAddress, newFeeRate, maxFee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -19,6 +19,9 @@ import (
|
||||
// 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
|
||||
|
||||
// The current minimal fee rate according to mempool standards
|
||||
const minFeeRate = 1.0
|
||||
|
||||
func (s *server) CreateUnsignedTransactions(_ context.Context, request *pb.CreateUnsignedTransactionsRequest) (
|
||||
*pb.CreateUnsignedTransactionsResponse, error,
|
||||
) {
|
||||
@ -34,23 +37,36 @@ func (s *server) CreateUnsignedTransactions(_ context.Context, request *pb.Creat
|
||||
return &pb.CreateUnsignedTransactionsResponse{UnsignedTransactions: unsignedTransactions}, nil
|
||||
}
|
||||
|
||||
func (s *server) calculateFeeRate(requestFeePolicy *pb.FeePolicy) (float64, error) {
|
||||
var feeRate float64
|
||||
func (s *server) calculateFeeLimits(requestFeePolicy *pb.FeePolicy) (feeRate float64, maxFee uint64, err error) {
|
||||
feeRate = minFeeRate
|
||||
maxFee = math.MaxUint64
|
||||
switch requestFeePolicy := requestFeePolicy.FeePolicy.(type) {
|
||||
case *pb.FeePolicy_ExactFeeRate:
|
||||
feeRate = requestFeePolicy.ExactFeeRate
|
||||
feeRate = math.Max(requestFeePolicy.ExactFeeRate, minFeeRate)
|
||||
case *pb.FeePolicy_MaxFeeRate:
|
||||
estimate, err := s.rpcClient.GetFeeEstimate()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return 0, 0, err
|
||||
}
|
||||
feeRate = math.Min(estimate.Estimate.NormalBuckets[0].Feerate, requestFeePolicy.MaxFeeRate)
|
||||
case *pb.FeePolicy_MaxFee:
|
||||
// TODO
|
||||
panic("todo")
|
||||
estimate, err := s.rpcClient.GetFeeEstimate()
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
feeRate = estimate.Estimate.NormalBuckets[0].Feerate
|
||||
maxFee = requestFeePolicy.MaxFee
|
||||
case nil:
|
||||
estimate, err := s.rpcClient.GetFeeEstimate()
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
feeRate = estimate.Estimate.NormalBuckets[0].Feerate
|
||||
// Default to a bound of max 1 KAS as fee
|
||||
maxFee = constants.SompiPerKaspa
|
||||
}
|
||||
|
||||
return feeRate, nil
|
||||
return feeRate, maxFee, nil
|
||||
}
|
||||
|
||||
func (s *server) createUnsignedTransactions(address string, amount uint64, isSendAll bool, fromAddressesString []string, useExistingChangeAddress bool, requestFeePolicy *pb.FeePolicy) ([][]byte, error) {
|
||||
@ -58,7 +74,7 @@ func (s *server) createUnsignedTransactions(address string, amount uint64, isSen
|
||||
return nil, errors.Errorf("wallet daemon is not synced yet, %s", s.formatSyncStateReport())
|
||||
}
|
||||
|
||||
feeRate, err := s.calculateFeeRate(requestFeePolicy)
|
||||
feeRate, maxFee, err := s.calculateFeeLimits(requestFeePolicy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -84,7 +100,7 @@ func (s *server) createUnsignedTransactions(address string, amount uint64, isSen
|
||||
return nil, err
|
||||
}
|
||||
|
||||
selectedUTXOs, spendValue, changeSompi, err := s.selectUTXOs(amount, isSendAll, feeRate, fromAddresses)
|
||||
selectedUTXOs, spendValue, changeSompi, err := s.selectUTXOs(amount, isSendAll, feeRate, maxFee, fromAddresses)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -110,19 +126,19 @@ func (s *server) createUnsignedTransactions(address string, amount uint64, isSen
|
||||
return nil, err
|
||||
}
|
||||
|
||||
unsignedTransactions, err := s.maybeAutoCompoundTransaction(unsignedTransaction, toAddress, changeAddress, changeWalletAddress, feeRate)
|
||||
unsignedTransactions, err := s.maybeAutoCompoundTransaction(unsignedTransaction, toAddress, changeAddress, changeWalletAddress, feeRate, maxFee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return unsignedTransactions, nil
|
||||
}
|
||||
|
||||
func (s *server) selectUTXOs(spendAmount uint64, isSendAll bool, feeRate float64, fromAddresses []*walletAddress) (
|
||||
func (s *server) selectUTXOs(spendAmount uint64, isSendAll bool, feeRate float64, maxFee uint64, fromAddresses []*walletAddress) (
|
||||
selectedUTXOs []*libkaspawallet.UTXO, totalReceived uint64, changeSompi uint64, err error) {
|
||||
return s.selectUTXOsWithPreselected(nil, map[externalapi.DomainOutpoint]struct{}{}, spendAmount, isSendAll, feeRate, fromAddresses)
|
||||
return s.selectUTXOsWithPreselected(nil, map[externalapi.DomainOutpoint]struct{}{}, spendAmount, isSendAll, feeRate, maxFee, fromAddresses)
|
||||
}
|
||||
|
||||
func (s *server) selectUTXOsWithPreselected(preSelectedUTXOs []*walletUTXO, allowUsed map[externalapi.DomainOutpoint]struct{}, spendAmount uint64, isSendAll bool, feeRate float64, fromAddresses []*walletAddress) (
|
||||
func (s *server) selectUTXOsWithPreselected(preSelectedUTXOs []*walletUTXO, allowUsed map[externalapi.DomainOutpoint]struct{}, spendAmount uint64, isSendAll bool, feeRate float64, maxFee uint64, fromAddresses []*walletAddress) (
|
||||
selectedUTXOs []*libkaspawallet.UTXO, totalReceived uint64, changeSompi uint64, err error) {
|
||||
|
||||
preSelectedSet := make(map[externalapi.DomainOutpoint]struct{})
|
||||
@ -168,7 +184,7 @@ func (s *server) selectUTXOsWithPreselected(preSelectedUTXOs []*walletUTXO, allo
|
||||
totalValue += utxo.UTXOEntry.Amount()
|
||||
|
||||
// We're overestimating a bit by assuming that any transaction will have a change output
|
||||
fee, err = s.estimateFee(selectedUTXOs, feeRate, true)
|
||||
fee, err = s.estimateFee(selectedUTXOs, feeRate, maxFee, true)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@ -227,7 +243,7 @@ func (s *server) selectUTXOsWithPreselected(preSelectedUTXOs []*walletUTXO, allo
|
||||
return selectedUTXOs, totalReceived, totalValue - totalSpend, nil
|
||||
}
|
||||
|
||||
func (s *server) estimateFee(selectedUTXOs []*libkaspawallet.UTXO, feeRate float64, assumeChange bool) (uint64, error) {
|
||||
func (s *server) estimateFee(selectedUTXOs []*libkaspawallet.UTXO, feeRate float64, maxFee uint64, assumeChange bool) (uint64, error) {
|
||||
fakePubKey := [util.PublicKeySize]byte{}
|
||||
fakeAddr, err := util.NewAddressPublicKey(fakePubKey[:], s.params.Prefix)
|
||||
if err != nil {
|
||||
@ -273,7 +289,7 @@ func (s *server) estimateFee(selectedUTXOs []*libkaspawallet.UTXO, feeRate float
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint64(float64(mass) * feeRate), nil
|
||||
return min(uint64(math.Ceil(float64(mass)*feeRate)), maxFee), nil
|
||||
}
|
||||
|
||||
func (s *server) estimateFeePerInput(feeRate float64) (uint64, error) {
|
||||
|
@ -22,8 +22,8 @@ import (
|
||||
// An additional `mergeTransaction` is generated - which merges the outputs of the above splits into a single output
|
||||
// paying to the original transaction's payee.
|
||||
func (s *server) maybeAutoCompoundTransaction(transaction *serialization.PartiallySignedTransaction, toAddress util.Address,
|
||||
changeAddress util.Address, changeWalletAddress *walletAddress, feeRate float64) ([][]byte, error) {
|
||||
splitTransactions, err := s.maybeSplitAndMergeTransaction(transaction, toAddress, changeAddress, changeWalletAddress, feeRate)
|
||||
changeAddress util.Address, changeWalletAddress *walletAddress, feeRate float64, maxFee uint64) ([][]byte, error) {
|
||||
splitTransactions, err := s.maybeSplitAndMergeTransaction(transaction, toAddress, changeAddress, changeWalletAddress, feeRate, maxFee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -44,6 +44,7 @@ func (s *server) mergeTransaction(
|
||||
changeAddress util.Address,
|
||||
changeWalletAddress *walletAddress,
|
||||
feeRate float64,
|
||||
maxFee uint64,
|
||||
) (*serialization.PartiallySignedTransaction, error) {
|
||||
numOutputs := len(originalTransaction.Tx.Outputs)
|
||||
if numOutputs > 2 || numOutputs == 0 {
|
||||
@ -70,7 +71,7 @@ func (s *server) mergeTransaction(
|
||||
totalValue += output.Value
|
||||
}
|
||||
// We're overestimating a bit by assuming that any transaction will have a change output
|
||||
fee, err := s.estimateFee(utxos, feeRate, true)
|
||||
fee, err := s.estimateFee(utxos, feeRate, maxFee, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -104,7 +105,7 @@ func (s *server) mergeTransaction(
|
||||
}
|
||||
|
||||
func (s *server) maybeSplitAndMergeTransaction(transaction *serialization.PartiallySignedTransaction, toAddress util.Address,
|
||||
changeAddress util.Address, changeWalletAddress *walletAddress, feeRate float64) ([]*serialization.PartiallySignedTransaction, error) {
|
||||
changeAddress util.Address, changeWalletAddress *walletAddress, feeRate float64, maxFee uint64) ([]*serialization.PartiallySignedTransaction, error) {
|
||||
|
||||
transactionMass, err := s.estimateComputeMassAfterSignatures(transaction)
|
||||
if err != nil {
|
||||
@ -115,7 +116,7 @@ func (s *server) maybeSplitAndMergeTransaction(transaction *serialization.Partia
|
||||
return []*serialization.PartiallySignedTransaction{transaction}, nil
|
||||
}
|
||||
|
||||
splitCount, inputCountPerSplit, err := s.splitAndInputPerSplitCounts(transaction, transactionMass, changeAddress, feeRate)
|
||||
splitCount, inputCountPerSplit, err := s.splitAndInputPerSplitCounts(transaction, transactionMass, changeAddress, feeRate, maxFee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -125,19 +126,19 @@ func (s *server) maybeSplitAndMergeTransaction(transaction *serialization.Partia
|
||||
startIndex := i * inputCountPerSplit
|
||||
endIndex := startIndex + inputCountPerSplit
|
||||
var err error
|
||||
splitTransactions[i], err = s.createSplitTransaction(transaction, changeAddress, startIndex, endIndex, feeRate)
|
||||
splitTransactions[i], err = s.createSplitTransaction(transaction, changeAddress, startIndex, endIndex, feeRate, maxFee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(splitTransactions) > 1 {
|
||||
mergeTransaction, err := s.mergeTransaction(splitTransactions, transaction, toAddress, changeAddress, changeWalletAddress, feeRate)
|
||||
mergeTransaction, err := s.mergeTransaction(splitTransactions, transaction, toAddress, changeAddress, changeWalletAddress, feeRate, maxFee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Recursion will be 2-3 iterations deep even in the rarest` cases, so considered safe..
|
||||
splitMergeTransaction, err := s.maybeSplitAndMergeTransaction(mergeTransaction, toAddress, changeAddress, changeWalletAddress, feeRate)
|
||||
splitMergeTransaction, err := s.maybeSplitAndMergeTransaction(mergeTransaction, toAddress, changeAddress, changeWalletAddress, feeRate, maxFee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -150,7 +151,7 @@ func (s *server) maybeSplitAndMergeTransaction(transaction *serialization.Partia
|
||||
|
||||
// splitAndInputPerSplitCounts calculates the number of splits to create, and the number of inputs to assign per split.
|
||||
func (s *server) splitAndInputPerSplitCounts(transaction *serialization.PartiallySignedTransaction, transactionMass uint64,
|
||||
changeAddress util.Address, feeRate float64) (splitCount, inputsPerSplitCount int, err error) {
|
||||
changeAddress util.Address, feeRate float64, maxFee uint64) (splitCount, inputsPerSplitCount int, err error) {
|
||||
|
||||
// Create a dummy transaction which is a clone of the original transaction, but without inputs,
|
||||
// to calculate how much mass do all the inputs have
|
||||
@ -170,7 +171,7 @@ func (s *server) splitAndInputPerSplitCounts(transaction *serialization.Partiall
|
||||
|
||||
// Create another dummy transaction, this time one similar to the split transactions we wish to generate,
|
||||
// but with 0 inputs, to calculate how much mass for inputs do we have available in the split transactions
|
||||
splitTransactionWithoutInputs, err := s.createSplitTransaction(transaction, changeAddress, 0, 0, feeRate)
|
||||
splitTransactionWithoutInputs, err := s.createSplitTransaction(transaction, changeAddress, 0, 0, feeRate, maxFee)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
@ -188,7 +189,7 @@ func (s *server) splitAndInputPerSplitCounts(transaction *serialization.Partiall
|
||||
}
|
||||
|
||||
func (s *server) createSplitTransaction(transaction *serialization.PartiallySignedTransaction,
|
||||
changeAddress util.Address, startIndex int, endIndex int, feeRate float64) (*serialization.PartiallySignedTransaction, error) {
|
||||
changeAddress util.Address, startIndex int, endIndex int, feeRate float64, maxFee uint64) (*serialization.PartiallySignedTransaction, error) {
|
||||
|
||||
selectedUTXOs := make([]*libkaspawallet.UTXO, 0, endIndex-startIndex)
|
||||
totalSompi := uint64(0)
|
||||
@ -206,7 +207,7 @@ func (s *server) createSplitTransaction(transaction *serialization.PartiallySign
|
||||
totalSompi += selectedUTXOs[i-startIndex].UTXOEntry.Amount()
|
||||
}
|
||||
if len(selectedUTXOs) != 0 {
|
||||
fee, err := s.estimateFee(selectedUTXOs, feeRate, false)
|
||||
fee, err := s.estimateFee(selectedUTXOs, feeRate, maxFee, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user