mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-11-24 14:35:53 +00:00
127 lines
3.6 KiB
Go
127 lines
3.6 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
"golang.org/x/exp/slices"
|
|
|
|
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
|
|
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
|
|
"github.com/kaspanet/kaspad/util"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
func (s *server) CreateUnsignedTransactionVerbose(_ context.Context, request *pb.CreateUnsignedTransactionVerboseRequest) (
|
|
*pb.CreateUnsignedTransactionsResponse, error) {
|
|
s.lock.Lock()
|
|
defer s.lock.Unlock()
|
|
|
|
inputs, err := protoOutputsToDomainOutputs(request.Inputs)
|
|
outputs, err := protoPaymentToLibPayment(request.Outputs, s.params.Prefix)
|
|
|
|
unsignedTransactions, err := s.createUnsignedTransactionVerbose(inputs, outputs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &pb.CreateUnsignedTransactionsResponse{UnsignedTransactions: unsignedTransactions}, nil
|
|
}
|
|
|
|
func (s *server) createUnsignedTransactionVerbose(inputs []externalapi.DomainOutpoint, payments []*libkaspawallet.Payment) ([][]byte, error) {
|
|
if !s.isSynced() {
|
|
return nil, errors.New("server is not synced")
|
|
}
|
|
|
|
err := s.refreshUTXOs()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
selectedUTXOs, totalValue, err := s.selectUTXOsByOutpoints(inputs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(selectedUTXOs) < len(inputs) {
|
|
return nil, errors.New("Some UTXOs are unavailable")
|
|
}
|
|
|
|
totalSpend := uint64(0)
|
|
for _, payment := range payments {
|
|
totalSpend += payment.Amount
|
|
}
|
|
|
|
changeSompi := totalValue - totalSpend - feePerInput*uint64(len(selectedUTXOs))
|
|
if changeSompi < 0 {
|
|
return nil, errors.New("Total input is not enough to cover total output and fees")
|
|
}
|
|
|
|
changeAddress, _, err := s.changeAddress()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if changeSompi > 0 {
|
|
payments = append(payments, &libkaspawallet.Payment{
|
|
Address: changeAddress,
|
|
Amount: changeSompi,
|
|
})
|
|
}
|
|
unsignedTransaction, err := libkaspawallet.CreateUnsignedTransaction(s.keysFile.ExtendedPublicKeys,
|
|
s.keysFile.MinimumSignatures,
|
|
payments, selectedUTXOs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return [][]byte{unsignedTransaction}, nil
|
|
}
|
|
|
|
func (s *server) selectUTXOsByOutpoints(inputs []externalapi.DomainOutpoint) (selectedUTXOs []*libkaspawallet.UTXO, totalValue uint64, err error) {
|
|
dagInfo, err := s.rpcClient.GetBlockDAGInfo()
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
for _, utxo := range s.utxosSortedByAmount {
|
|
if !slices.Contains(inputs, *utxo.Outpoint) ||
|
|
!isUTXOSpendable(utxo, dagInfo.VirtualDAAScore, s.params.BlockCoinbaseMaturity) {
|
|
continue
|
|
}
|
|
|
|
selectedUTXOs = append(selectedUTXOs, &libkaspawallet.UTXO{
|
|
Outpoint: utxo.Outpoint,
|
|
UTXOEntry: utxo.UTXOEntry,
|
|
DerivationPath: s.walletAddressPath(utxo.address),
|
|
})
|
|
totalValue += utxo.UTXOEntry.Amount()
|
|
}
|
|
return selectedUTXOs, totalValue, nil
|
|
}
|
|
|
|
func protoOutputsToDomainOutputs(request_inputs []*pb.Outpoint) (inputs []externalapi.DomainOutpoint, err error) {
|
|
for _, input := range request_inputs {
|
|
txId, err := externalapi.NewDomainTransactionIDFromString(input.TransactionId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
inputs = append(inputs, externalapi.DomainOutpoint{
|
|
TransactionID: *txId,
|
|
Index: input.Index,
|
|
})
|
|
}
|
|
return inputs, nil
|
|
}
|
|
|
|
func protoPaymentToLibPayment(request_outputs []*pb.PaymentOutput, prefix util.Bech32Prefix) (outputs []*libkaspawallet.Payment, err error) {
|
|
for _, output := range request_outputs {
|
|
address, err := util.DecodeAddress(output.Address, prefix)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
outputs = append(outputs, &libkaspawallet.Payment{
|
|
Address: address,
|
|
Amount: output.Amount,
|
|
})
|
|
}
|
|
return outputs, nil
|
|
}
|