diff --git a/app/appmessage/rpc_submit_transaction_replacement.go b/app/appmessage/rpc_submit_transaction_replacement.go index e2bc4585d1..9bd4c264d6 100644 --- a/app/appmessage/rpc_submit_transaction_replacement.go +++ b/app/appmessage/rpc_submit_transaction_replacement.go @@ -23,7 +23,8 @@ func NewSubmitTransactionReplacementRequestMessage(transaction *RPCTransaction) // its respective RPC message type SubmitTransactionReplacementResponseMessage struct { baseMessage - TransactionID string + TransactionID string + ReplacedTransaction *RPCTransaction Error *RPCError } diff --git a/cmd/kaspawallet/daemon/server/bump_fee.go b/cmd/kaspawallet/daemon/server/bump_fee.go index 576b2e0faf..7b77339980 100644 --- a/cmd/kaspawallet/daemon/server/bump_fee.go +++ b/cmd/kaspawallet/daemon/server/bump_fee.go @@ -37,13 +37,11 @@ func (s *server) BumpFee(_ context.Context, request *pb.BumpFeeRequest) (*pb.Bum } outpointsSet := make(map[externalapi.DomainOutpoint]struct{}) + var maxUTXO *walletUTXO for _, input := range domainTx.Inputs { outpointsSet[input.PreviousOutpoint] = struct{}{} - } - - var maxUTXO *walletUTXO - for _, utxo := range s.utxosSortedByAmount { - if _, ok := outpointsSet[*utxo.Outpoint]; !ok { + utxo, ok := s.mempoolExcludedUTXOs[input.PreviousOutpoint] + if !ok { continue } @@ -52,6 +50,19 @@ func (s *server) BumpFee(_ context.Context, request *pb.BumpFeeRequest) (*pb.Bum } } + if maxUTXO == nil { + // If we got here it means for some reason s.mempoolExcludedUTXOs is not up to date and we need to search for the UTXOs in s.utxosSortedByAmount + for _, utxo := range s.utxosSortedByAmount { + if _, ok := outpointsSet[*utxo.Outpoint]; !ok { + continue + } + + if maxUTXO == nil || utxo.UTXOEntry.Amount() > maxUTXO.UTXOEntry.Amount() { + maxUTXO = utxo + } + } + } + if maxUTXO == nil { return nil, errors.Errorf("no UTXOs were found for transaction %s. This probably means the transaction is already accepted", request.TxID) } diff --git a/cmd/kaspawallet/daemon/server/server.go b/cmd/kaspawallet/daemon/server/server.go index 1b43dac8cf..ff649ea079 100644 --- a/cmd/kaspawallet/daemon/server/server.go +++ b/cmd/kaspawallet/daemon/server/server.go @@ -8,6 +8,7 @@ import ( "sync/atomic" "time" + "github.com/kaspanet/kaspad/app/appmessage" "github.com/kaspanet/kaspad/version" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" @@ -37,6 +38,7 @@ type server struct { lock sync.RWMutex utxosSortedByAmount []*walletUTXO + mempoolExcludedUTXOs map[externalapi.DomainOutpoint]*walletUTXO nextSyncStartIndex uint32 keysFile *keys.File shutdown chan struct{} @@ -111,6 +113,7 @@ func Start(params *dagconfig.Params, listen, rpcServer string, keysFilePath stri params: params, coinbaseMaturity: coinbaseMaturity, utxosSortedByAmount: []*walletUTXO{}, + mempoolExcludedUTXOs: map[appmessage.RPCOutpoint]struct{}{}, nextSyncStartIndex: 0, keysFile: keysFile, shutdown: make(chan struct{}), diff --git a/cmd/kaspawallet/daemon/server/sync.go b/cmd/kaspawallet/daemon/server/sync.go index 6aa7bd2b4e..427592e1a7 100644 --- a/cmd/kaspawallet/daemon/server/sync.go +++ b/cmd/kaspawallet/daemon/server/sync.go @@ -6,6 +6,7 @@ import ( "time" "github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet" + "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/app/appmessage" "github.com/pkg/errors" @@ -240,11 +241,8 @@ func (s *server) updateUTXOSet(entries []*appmessage.UTXOsByAddressesEntry, memp } } + mempoolExcludedUTXOs := make(map[externalapi.DomainOutpoint]*walletUTXO) for _, entry := range entries { - if _, ok := exclude[*entry.Outpoint]; ok { - continue - } - outpoint, err := appmessage.RPCOutpointToDomainOutpoint(entry.Outpoint) if err != nil { return err @@ -260,11 +258,22 @@ func (s *server) updateUTXOSet(entries []*appmessage.UTXOsByAddressesEntry, memp if !ok { return errors.Errorf("Got result from address %s even though it wasn't requested", entry.Address) } - utxos = append(utxos, &walletUTXO{ + + utxo := &walletUTXO{ Outpoint: outpoint, UTXOEntry: utxoEntry, address: address, - }) + } + + if _, ok := exclude[*entry.Outpoint]; ok { + mempoolExcludedUTXOs[*outpoint] = utxo + } else { + utxos = append(utxos, &walletUTXO{ + Outpoint: outpoint, + UTXOEntry: utxoEntry, + address: address, + }) + } } sort.Slice(utxos, func(i, j int) bool { return utxos[i].UTXOEntry.Amount() > utxos[j].UTXOEntry.Amount() }) @@ -272,6 +281,7 @@ func (s *server) updateUTXOSet(entries []*appmessage.UTXOsByAddressesEntry, memp s.lock.Lock() s.startTimeOfLastCompletedRefresh = refreshStart s.utxosSortedByAmount = utxos + s.mempoolExcludedUTXOs = mempoolExcludedUTXOs // Cleanup expired used outpoints to avoid a memory leak for outpoint, broadcastTime := range s.usedOutpoints { diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_submit_transaction_replacement.go b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_submit_transaction_replacement.go index dc621ac75b..2e8cf8b5be 100644 --- a/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_submit_transaction_replacement.go +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_submit_transaction_replacement.go @@ -46,8 +46,12 @@ func (x *KaspadMessage_SubmitTransactionReplacementResponse) fromAppMessage(mess err = &RPCError{Message: message.Error.Message} } x.SubmitTransactionReplacementResponse = &SubmitTransactionReplacementResponseMessage{ - TransactionId: message.TransactionID, - Error: err, + TransactionId: message.TransactionID, + ReplacedTransaction: &RpcTransaction{}, + Error: err, + } + if message.ReplacedTransaction != nil { + x.SubmitTransactionReplacementResponse.ReplacedTransaction.fromAppMessage(message.ReplacedTransaction) } return nil } @@ -61,8 +65,13 @@ func (x *SubmitTransactionReplacementResponseMessage) toAppMessage() (appmessage if err != nil && !errors.Is(err, errorNil) { return nil, err } + replacedTx, err := x.ReplacedTransaction.toAppMessage() + if err != nil { + return nil, err + } return &appmessage.SubmitTransactionReplacementResponseMessage{ - TransactionID: x.TransactionId, - Error: rpcErr, + TransactionID: x.TransactionId, + ReplacedTransaction: replacedTx, + Error: rpcErr, }, nil }