From 7280bfff2a720df06eba2c3b0794d1c2994f51ac Mon Sep 17 00:00:00 2001 From: Collins Olanrewaju Date: Mon, 7 Jun 2021 08:56:04 +0100 Subject: [PATCH 1/7] Add SelectUnspent json rpc command --- internal/rpc/jsonrpc/methods.go | 41 +++++ internal/rpchelp/methods.go | 1 + rpc/client/dcrwallet/methods.go | 7 + rpc/jsonrpc/types/methods.go | 24 +++ rpc/jsonrpc/types/results.go | 9 ++ wallet/wallet.go | 258 ++++++++++++++++++++++++++++++++ 6 files changed, 340 insertions(+) diff --git a/internal/rpc/jsonrpc/methods.go b/internal/rpc/jsonrpc/methods.go index e30fd4eff..cd26b8696 100644 --- a/internal/rpc/jsonrpc/methods.go +++ b/internal/rpc/jsonrpc/methods.go @@ -136,6 +136,7 @@ var handlers = map[string]handler{ "listsinceblock": {fn: (*Server).listSinceBlock}, "listtransactions": {fn: (*Server).listTransactions}, "listunspent": {fn: (*Server).listUnspent}, + "selectunspent": {fn: (*Server).selectUnspent}, "lockaccount": {fn: (*Server).lockAccount}, "lockunspent": {fn: (*Server).lockUnspent}, "mixaccount": {fn: (*Server).mixAccount}, @@ -3081,6 +3082,46 @@ func (s *Server) listUnspent(ctx context.Context, icmd interface{}) (interface{} return result, nil } +func (s *Server) selectUnspent(ctx context.Context, icmd interface{}) (interface{}, error) { + cmd := icmd.(*types.SelectUnspentCmd) + w, ok := s.walletLoader.LoadedWallet() + if !ok { + return nil, errUnloadedWallet + } + + targetAmount, err := dcrutil.NewAmount(cmd.TargetAmount) + if err != nil { + return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err) + } + if targetAmount < 0 { + return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "negative target amount") + } + + minAmount, err := dcrutil.NewAmount(cmd.MinAmount) + if err != nil { + return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err) + } + if minAmount < 0 { + return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "negative min amount") + } + + if minAmount > targetAmount { + return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "target amount is less than min amount") + } + + seenTxAddress := make(map[string]struct{}) + if cmd.SeenTxAddress != nil { + seenTxAddress = *cmd.SeenTxAddress + } + + result, err := w.SelectUnspent(ctx, targetAmount, minAmount, int32(cmd.MinConf), cmd.AccountName, + cmd.SpendAll, seenTxAddress, types.InputSelectionMethod(cmd.InputSelectionMethod)) + if err != nil { + return nil, err + } + return result, nil +} + // lockUnspent handles the lockunspent command. func (s *Server) lockUnspent(ctx context.Context, icmd interface{}) (interface{}, error) { cmd := icmd.(*types.LockUnspentCmd) diff --git a/internal/rpchelp/methods.go b/internal/rpchelp/methods.go index ce305a2cb..4829c4555 100644 --- a/internal/rpchelp/methods.go +++ b/internal/rpchelp/methods.go @@ -84,6 +84,7 @@ var Methods = []struct { {"listsinceblock", []interface{}{(*types.ListSinceBlockResult)(nil)}}, {"listtransactions", returnsLTRArray}, {"listunspent", []interface{}{(*types.ListUnspentResult)(nil)}}, + {"selectunspent", []interface{}{(*types.ListUnspentResult)(nil)}}, {"lockaccount", nil}, {"lockunspent", returnsBool}, {"mixaccount", nil}, diff --git a/rpc/client/dcrwallet/methods.go b/rpc/client/dcrwallet/methods.go index 063c504ef..ee253df50 100644 --- a/rpc/client/dcrwallet/methods.go +++ b/rpc/client/dcrwallet/methods.go @@ -94,6 +94,13 @@ func (c *Client) ListUnspentMinMaxAddresses(ctx context.Context, minConf, maxCon return res, err } +func (c *Client) SelectUnspent(ctx context.Context, minConf int, targetAmount, minAmount dcrutil.Amount, + accountName string, spendAll bool, inputSelectionMethod string, seenTxAddress map[string]struct{}) ([]types.ListUnspentResult, error) { + var res []types.ListUnspentResult + err := c.Call(ctx, "selectunspent", &res, minConf, targetAmount.ToCoin(), minAmount.ToCoin(), accountName, spendAll, inputSelectionMethod, seenTxAddress) + return res, err +} + // ListSinceBlock returns all transactions added in blocks since the specified // block hash, or all transactions if it is nil, using the default number of // minimum confirmations as a filter. diff --git a/rpc/jsonrpc/types/methods.go b/rpc/jsonrpc/types/methods.go index 23c5325a2..5b9f19331 100644 --- a/rpc/jsonrpc/types/methods.go +++ b/rpc/jsonrpc/types/methods.go @@ -659,6 +659,29 @@ func NewListUnspentCmd(minConf, maxConf *int, addresses *[]string) *ListUnspentC } } +type SelectUnspentCmd struct { + MinConf int + TargetAmount float64 + MinAmount float64 + AccountName string + SpendAll bool + InputSelectionMethod string + SeenTxAddress *map[string]struct{} +} + +func NewSelectUnspentCmd(minConf int, targetAmount, minAmount float64, accountName string, + spendAll bool, inputSelectionMethod string, seenTxAddress *map[string]struct{}) *SelectUnspentCmd { + return &SelectUnspentCmd{ + MinConf: minConf, + TargetAmount: targetAmount, + MinAmount: minAmount, + AccountName: accountName, + SpendAll: spendAll, + InputSelectionMethod: inputSelectionMethod, + SeenTxAddress: seenTxAddress, + } +} + // LockUnspentCmd defines the lockunspent JSON-RPC command. type LockUnspentCmd struct { Unlock bool @@ -1234,6 +1257,7 @@ func init() { {"listsinceblock", (*ListSinceBlockCmd)(nil)}, {"listtransactions", (*ListTransactionsCmd)(nil)}, {"listunspent", (*ListUnspentCmd)(nil)}, + {"selectunspent", (*SelectUnspentCmd)(nil)}, {"lockaccount", (*LockAccountCmd)(nil)}, {"lockunspent", (*LockUnspentCmd)(nil)}, {"mixaccount", (*MixAccountCmd)(nil)}, diff --git a/rpc/jsonrpc/types/results.go b/rpc/jsonrpc/types/results.go index 8d6aa34c5..4167a8e00 100644 --- a/rpc/jsonrpc/types/results.go +++ b/rpc/jsonrpc/types/results.go @@ -265,6 +265,15 @@ type ListSinceBlockResult struct { LastBlock string `json:"lastblock"` } +type InputSelectionMethod string + +const ( + RandomInputSelection InputSelectionMethod = "random" + RandomAddressInputSelection InputSelectionMethod = "randomaddress" + OneUTXOInputSelection InputSelectionMethod = "oneutxo" + UniqueTxInputSelection InputSelectionMethod = "uniquetx" +) + // ListUnspentResult models a successful response from the listunspent request. // Contains Decred additions. type ListUnspentResult struct { diff --git a/wallet/wallet.go b/wallet/wallet.go index 0e389eb2a..becdec35d 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -3716,6 +3716,264 @@ func (w *Wallet) ListUnspent(ctx context.Context, minconf, maxconf int32, addres return results, nil } +func (w *Wallet) SelectUnspent(ctx context.Context, targetAmount, minAmount dcrutil.Amount, minconf int32, accountName string, + spendAll bool, seenTxIDs map[string]struct{}, inputSelectionMethod types.InputSelectionMethod) ([]*types.ListUnspentResult, error) { + const op errors.Op = "wallet.SelectUnspent" + + var ( + currentTotal dcrutil.Amount + results []*types.ListUnspentResult + ) + + err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error { + addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey) + txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey) + + _, tipHeight := w.txStore.MainChainTip(dbtx) + + unspent, err := w.txStore.UnspentOutputs(dbtx) + if err != nil { + return err + } + sort.Sort(sort.Reverse(creditSlice(unspent))) + + defaultAccountName, err := w.manager.AccountName( + addrmgrNs, udb.DefaultAccountNum) + if err != nil { + return err + } + + // used for RandomAddressInputSelection + var randomUnspent *types.ListUnspentResult + + for i := range unspent { + output := unspent[i] + + if output.Amount < minAmount { + log.Infof("Skipping utxo %s, amount: %s, min: %s", output.Hash.String(), output.Amount, minAmount) + continue + } + + if inputSelectionMethod == types.OneUTXOInputSelection { + // We're selecting only one utxo so this loop will run until we find + // a single utxo that can pay the target amount. + + if targetAmount > output.Amount { + // continue if this utxo cannot pay the require amount + continue + } + } else if inputSelectionMethod == types.UniqueTxInputSelection { + // skip duplicate tx id + _, seenTxID := seenTxIDs[output.Hash.String()] + if seenTxID { + log.Infof("Skipping duplicate txid: %s:%d", output.Hash.String(), output.Index) + continue + } + } + + details, err := w.txStore.TxDetails(txmgrNs, &output.Hash) + if err != nil { + return err + } + + // Outputs with fewer confirmations than the minimum are excluded. + confs := confirms(output.Height, tipHeight) + if confs < minconf { + continue + } + + // Only mature coinbase outputs are included. + if output.FromCoinBase { + if !coinbaseMatured(w.chainParams, output.Height, tipHeight) { + continue + } + } + + switch details.TxRecord.TxType { + case stake.TxTypeSStx: + // Ticket commitment, only spendable after ticket maturity. + if output.Index == 0 { + if !ticketMatured(w.chainParams, details.Height(), tipHeight) { + continue + } + } + // Change outputs. + if (output.Index > 0) && (output.Index%2 == 0) { + if !ticketChangeMatured(w.chainParams, details.Height(), tipHeight) { + continue + } + } + case stake.TxTypeSSGen: + // All non-OP_RETURN outputs for SSGen tx are only spendable + // after coinbase maturity many blocks. + if !coinbaseMatured(w.chainParams, details.Height(), tipHeight) { + continue + } + case stake.TxTypeSSRtx: + // All outputs for SSRtx tx are only spendable + // after coinbase maturity many blocks. + if !coinbaseMatured(w.chainParams, details.Height(), tipHeight) { + continue + } + } + + // Exclude locked outputs from the result set. + if w.LockedOutpoint(&output.OutPoint.Hash, output.OutPoint.Index) { + continue + } + + // Lookup the associated account for the output. Use the + // default account name in case there is no associated account + // for some reason, although this should never happen. + // + // This will be unnecessary once transactions and outputs are + // grouped under the associated account in the db. + acctName := defaultAccountName + sc, addrs, _, err := txscript.ExtractPkScriptAddrs( + 0, output.PkScript, w.chainParams, true) // Yes treasury + if err != nil { + continue + } + if len(addrs) > 0 { + acct, err := w.manager.AddrAccount( + addrmgrNs, addrs[0]) + if err == nil { + s, err := w.manager.AccountName( + addrmgrNs, acct) + if err == nil { + acctName = s + } + } + + if inputSelectionMethod == types.UniqueTxInputSelection { + // skip duplicate address + _, seenAddress := seenTxIDs[addrs[0].String()] + if seenAddress { + log.Info("Skipping duplicate address:", addrs[0].String()) + continue + } + } + } + + if accountName != "" && accountName != acctName { + continue + } + + if randomUnspent != nil { // random address input selection + for _, addr := range addrs { + if randomUnspent.Address == addr.String() { + goto include + } + } + + continue + } + + include: + // At the moment watch-only addresses are not supported, so all + // recorded outputs that are not multisig are "spendable". + // Multisig outputs are only "spendable" if all keys are + // controlled by this wallet. + // + // TODO: Each case will need updates when watch-only addrs + // is added. For P2PK, P2PKH, and P2SH, the address must be + // looked up and not be watching-only. For multisig, all + // pubkeys must belong to the manager with the associated + // private key (currently it only checks whether the pubkey + // exists, since the private key is required at the moment). + var spendable bool + var redeemScript []byte + scSwitch: + switch sc { + case txscript.PubKeyHashTy: + spendable = true + case txscript.PubKeyTy: + spendable = true + case txscript.ScriptHashTy: + spendable = true + if len(addrs) != 1 { + return errors.Errorf("invalid address count for pay-to-script-hash output") + } + redeemScript, err = w.manager.RedeemScript(addrmgrNs, addrs[0]) + if err != nil { + return err + } + case txscript.StakeGenTy: + spendable = true + case txscript.StakeRevocationTy: + spendable = true + case txscript.StakeSubChangeTy: + spendable = true + case txscript.MultiSigTy: + for _, a := range addrs { + _, err := w.manager.Address(addrmgrNs, a) + if err == nil { + continue + } + if errors.Is(err, errors.NotExist) { + break scSwitch + } + return err + } + spendable = true + } + + if !spendable { + continue + } + + result := &types.ListUnspentResult{ + TxID: output.OutPoint.Hash.String(), + Vout: output.OutPoint.Index, + Tree: output.OutPoint.Tree, + Account: acctName, + ScriptPubKey: hex.EncodeToString(output.PkScript), + RedeemScript: hex.EncodeToString(redeemScript), + TxType: int(details.TxType), + Amount: output.Amount.ToCoin(), + Confirmations: int64(confs), + Spendable: spendable, + } + + if len(addrs) > 0 { + result.Address = addrs[0].String() + seenTxIDs[result.Address] = struct{}{} + } + results = append(results, result) + + currentTotal += output.Amount + seenTxIDs[result.TxID] = struct{}{} + + if inputSelectionMethod == types.RandomAddressInputSelection && randomUnspent == nil { + randomUnspent = result + } + + if inputSelectionMethod == types.OneUTXOInputSelection { + return nil + } else if currentTotal >= targetAmount { + if spendAll { + continue + } + + return nil + } + } + + if inputSelectionMethod == types.RandomAddressInputSelection { + return fmt.Errorf("insufficient balance, selected address does not have enough utxos to pay %s", targetAmount) + } else if inputSelectionMethod == types.OneUTXOInputSelection { + return fmt.Errorf("insufficient balance, no utxo is available to pay %s", targetAmount) + } + + return nil + }) + + if err != nil { + return nil, errors.E(op, err) + } + return results, nil +} + func (w *Wallet) LoadPrivateKey(ctx context.Context, addr stdaddr.Address) (key *secp256k1.PrivateKey, zero func(), err error) { From 6b7a0d63a31de351d21f6bcf3d426784a2b10019 Mon Sep 17 00:00:00 2001 From: Olanrewaju Collins Date: Wed, 11 Aug 2021 09:44:17 +0100 Subject: [PATCH 2/7] Add comments --- internal/rpc/jsonrpc/methods.go | 1 + rpc/jsonrpc/types/results.go | 14 +++++++++++--- wallet/wallet.go | 13 +++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/internal/rpc/jsonrpc/methods.go b/internal/rpc/jsonrpc/methods.go index cd26b8696..fe25cf4d5 100644 --- a/internal/rpc/jsonrpc/methods.go +++ b/internal/rpc/jsonrpc/methods.go @@ -3082,6 +3082,7 @@ func (s *Server) listUnspent(ctx context.Context, icmd interface{}) (interface{} return result, nil } +// selectUnspent handles the selectunspent command. func (s *Server) selectUnspent(ctx context.Context, icmd interface{}) (interface{}, error) { cmd := icmd.(*types.SelectUnspentCmd) w, ok := s.walletLoader.LoadedWallet() diff --git a/rpc/jsonrpc/types/results.go b/rpc/jsonrpc/types/results.go index 4167a8e00..ff0ee85b0 100644 --- a/rpc/jsonrpc/types/results.go +++ b/rpc/jsonrpc/types/results.go @@ -265,13 +265,21 @@ type ListSinceBlockResult struct { LastBlock string `json:"lastblock"` } +// InputSelectionMethod defines the type used in the selectunspent JSON-RPC +// result for the InputSelectionMethod command field. type InputSelectionMethod string const ( - RandomInputSelection InputSelectionMethod = "random" + // RandomInputSelection indicates any random utxo can be selected. + RandomInputSelection InputSelectionMethod = "random" + // RandomAddressInputSelection indicates utxos should be selected from + // one randomly selected address. RandomAddressInputSelection InputSelectionMethod = "randomaddress" - OneUTXOInputSelection InputSelectionMethod = "oneutxo" - UniqueTxInputSelection InputSelectionMethod = "uniquetx" + // OneUTXOInputSelection indicates only one utxo should be selected. + OneUTXOInputSelection InputSelectionMethod = "oneutxo" + // UniqueTxInputSelection indicates only utxos with unique address + // and hash should be selected. + UniqueTxInputSelection InputSelectionMethod = "uniquetx" ) // ListUnspentResult models a successful response from the listunspent request. diff --git a/wallet/wallet.go b/wallet/wallet.go index becdec35d..26e101fae 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -11,6 +11,7 @@ import ( "encoding/binary" "encoding/hex" "fmt" + "math/rand" "runtime" "sort" "strconv" @@ -3716,6 +3717,14 @@ func (w *Wallet) ListUnspent(ctx context.Context, minconf, maxconf int32, addres return results, nil } +// SelectUnspent returns a slice of objects representing the unspent wallet +// transactions fitting the given criteria and enough to pay the target amount. +// The transaction amount and confirmations will be more than the amount & minconf +// parameter, only transactions matching the accountName will be returned if it's +// not empty and the targetAmount is ignored if spendAll is true. The +// inputSelectionMethod method determines how and what inputs should be selected +// and the seenTxIDs can only be used with UniqueTxInputSelection to determine what +// transaction hash or address should be skipped. func (w *Wallet) SelectUnspent(ctx context.Context, targetAmount, minAmount dcrutil.Amount, minconf int32, accountName string, spendAll bool, seenTxIDs map[string]struct{}, inputSelectionMethod types.InputSelectionMethod) ([]*types.ListUnspentResult, error) { const op errors.Op = "wallet.SelectUnspent" @@ -3743,6 +3752,10 @@ func (w *Wallet) SelectUnspent(ctx context.Context, targetAmount, minAmount dcru return err } + // Shuffe utxos + rand.Seed(time.Now().UnixNano()) + rand.Shuffle(len(unspent), func(i, j int) { unspent[i], unspent[j] = unspent[j], unspent[i] }) + // used for RandomAddressInputSelection var randomUnspent *types.ListUnspentResult From e2be7ed832b852e57f372e8e7bf51c9aa6b044ee Mon Sep 17 00:00:00 2001 From: Olanrewaju Collins Date: Wed, 11 Aug 2021 12:14:00 +0100 Subject: [PATCH 3/7] Add json rpc help text --- internal/rpc/jsonrpc/methods.go | 29 ++++++++++++++++++++++----- internal/rpc/jsonrpc/rpcserverhelp.go | 3 ++- internal/rpchelp/helpdescs_en_US.go | 13 ++++++++++++ rpc/client/dcrwallet/methods.go | 6 +++--- rpc/jsonrpc/types/methods.go | 20 +++++++++--------- rpc/jsonrpc/types/results.go | 8 ++++---- 6 files changed, 56 insertions(+), 23 deletions(-) diff --git a/internal/rpc/jsonrpc/methods.go b/internal/rpc/jsonrpc/methods.go index fe25cf4d5..c021b3b29 100644 --- a/internal/rpc/jsonrpc/methods.go +++ b/internal/rpc/jsonrpc/methods.go @@ -3098,10 +3098,14 @@ func (s *Server) selectUnspent(ctx context.Context, icmd interface{}) (interface return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "negative target amount") } - minAmount, err := dcrutil.NewAmount(cmd.MinAmount) - if err != nil { - return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err) + var minAmount dcrutil.Amount + if cmd.MinAmount != nil { + minAmount, err = dcrutil.NewAmount(*cmd.MinAmount) + if err != nil { + return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err) + } } + if minAmount < 0 { return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "negative min amount") } @@ -3110,13 +3114,28 @@ func (s *Server) selectUnspent(ctx context.Context, icmd interface{}) (interface return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "target amount is less than min amount") } + var account string + if cmd.Account != nil { + account = *cmd.Account + } + + var spendAll = false + if cmd.SpendAll != nil { + spendAll = *cmd.SpendAll + } + + var inputMethod = types.RandomInputSelection + if cmd.InputSelectionMethod != nil { + inputMethod = types.InputSelectionMethod(*cmd.InputSelectionMethod) + } + seenTxAddress := make(map[string]struct{}) if cmd.SeenTxAddress != nil { seenTxAddress = *cmd.SeenTxAddress } - result, err := w.SelectUnspent(ctx, targetAmount, minAmount, int32(cmd.MinConf), cmd.AccountName, - cmd.SpendAll, seenTxAddress, types.InputSelectionMethod(cmd.InputSelectionMethod)) + result, err := w.SelectUnspent(ctx, targetAmount, minAmount, int32(*cmd.MinConf), account, + spendAll, seenTxAddress, inputMethod) if err != nil { return nil, err } diff --git a/internal/rpc/jsonrpc/rpcserverhelp.go b/internal/rpc/jsonrpc/rpcserverhelp.go index dc2879032..eaedd95fe 100644 --- a/internal/rpc/jsonrpc/rpcserverhelp.go +++ b/internal/rpc/jsonrpc/rpcserverhelp.go @@ -61,6 +61,7 @@ func helpDescsEnUS() map[string]string { "listsinceblock": "listsinceblock (\"blockhash\" targetconfirmations=1 includewatchonly=false)\n\nReturns a JSON array of objects listing details of all wallet transactions after some block.\n\nArguments:\n1. blockhash (string, optional) Hash of the parent block of the first block to consider transactions from, or unset to list all transactions\n2. targetconfirmations (numeric, optional, default=1) Minimum number of block confirmations of the last block in the result object. Must be 1 or greater. Note: The transactions array in the result object is not affected by this parameter\n3. includewatchonly (boolean, optional, default=false) Unused\n\nResult:\n{\n \"transactions\": [{ (array of object) JSON array of objects containing verbose details of the each transaction\n \"account\": \"value\", (string) DEPRECATED -- Unset\n \"address\": \"value\", (string) Payment address for a transaction output\n \"amount\": n.nnn, (numeric) The value of the transaction output valued in decred\n \"blockhash\": \"value\", (string) The hash of the block this transaction is mined in, or the empty string if unmined\n \"blockindex\": n, (numeric) Unset\n \"blocktime\": n, (numeric) The Unix time of the block header this transaction is mined in, or 0 if unmined\n \"category\": \"value\", (string) The kind of transaction: \"send\" for sent transactions, \"immature\" for immature coinbase outputs, \"generate\" for mature coinbase outputs, or \"recv\" for all other received outputs. Note: A single output may be included multiple times under different categories\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction\n \"fee\": n.nnn, (numeric) The total input value minus the total output value for sent transactions\n \"generated\": true|false, (boolean) Whether the transaction output is a coinbase output\n \"involveswatchonly\": true|false, (boolean) Unset\n \"time\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"timereceived\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"txid\": \"value\", (string) The hash of the transaction\n \"txtype\": \"value\", (string) The type of tx (regular tx, stake tx)\n \"vout\": n, (numeric) The transaction output index\n \"walletconflicts\": [\"value\",...], (array of string) Unset\n \"comment\": \"value\", (string) Unset\n \"otheraccount\": \"value\", (string) Unset\n },...], \n \"lastblock\": \"value\", (string) Hash of the latest-synced block to be used in later calls to listsinceblock\n} \n", "listtransactions": "listtransactions (\"account\" count=10 from=0 includewatchonly=false)\n\nReturns a JSON array of objects containing verbose details for wallet transactions.\n\nArguments:\n1. account (string, optional) DEPRECATED -- Unused (must be unset or \"*\")\n2. count (numeric, optional, default=10) Maximum number of transactions to create results from\n3. from (numeric, optional, default=0) Number of transactions to skip before results are created\n4. includewatchonly (boolean, optional, default=false) Unused\n\nResult:\n[{\n \"account\": \"value\", (string) DEPRECATED -- Unset\n \"address\": \"value\", (string) Payment address for a transaction output\n \"amount\": n.nnn, (numeric) The value of the transaction output valued in decred\n \"blockhash\": \"value\", (string) The hash of the block this transaction is mined in, or the empty string if unmined\n \"blockindex\": n, (numeric) Unset\n \"blocktime\": n, (numeric) The Unix time of the block header this transaction is mined in, or 0 if unmined\n \"category\": \"value\", (string) The kind of transaction: \"send\" for sent transactions, \"immature\" for immature coinbase outputs, \"generate\" for mature coinbase outputs, or \"recv\" for all other received outputs. Note: A single output may be included multiple times under different categories\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction\n \"fee\": n.nnn, (numeric) The total input value minus the total output value for sent transactions\n \"generated\": true|false, (boolean) Whether the transaction output is a coinbase output\n \"involveswatchonly\": true|false, (boolean) Unset\n \"time\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"timereceived\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"txid\": \"value\", (string) The hash of the transaction\n \"txtype\": \"value\", (string) The type of tx (regular tx, stake tx)\n \"vout\": n, (numeric) The transaction output index\n \"walletconflicts\": [\"value\",...], (array of string) Unset\n \"comment\": \"value\", (string) Unset\n \"otheraccount\": \"value\", (string) Unset\n},...]\n", "listunspent": "listunspent (minconf=1 maxconf=9999999 [\"address\",...] \"account\")\n\nReturns a JSON array of objects representing unlocked unspent outputs controlled by wallet keys.\n\nArguments:\n1. minconf (numeric, optional, default=1) Minimum number of block confirmations required before a transaction output is considered\n2. maxconf (numeric, optional, default=9999999) Maximum number of block confirmations required before a transaction output is excluded\n3. addresses (array of string, optional) If set, limits the returned details to unspent outputs received by any of these payment addresses\n4. account (string, optional) If set, only return unspent outputs from this account\n\nResult:\n{\n \"txid\": \"value\", (string) The transaction hash of the referenced output\n \"vout\": n, (numeric) The output index of the referenced output\n \"tree\": n, (numeric) The tree the transaction comes from\n \"txtype\": n, (numeric) The type of the transaction\n \"address\": \"value\", (string) The payment address that received the output\n \"account\": \"value\", (string) The account associated with the receiving payment address\n \"scriptPubKey\": \"value\", (string) The output script encoded as a hexadecimal string\n \"redeemScript\": \"value\", (string) The redeemScript if scriptPubKey is P2SH\n \"amount\": n.nnn, (numeric) The amount of the output valued in decred\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction\n \"spendable\": true|false, (boolean) Whether the output is entirely controlled by wallet keys/scripts (false for partially controlled multisig outputs or outputs to watch-only addresses)\n} \n", + "selectunspent": "selectunspent targetamount (minamount=0 minconf=1 account=\"\" spendall=false inputselectionmethod=\"random\" {\"address\":{},\"txhash\":{},...})\n\nReturns a JSON array of objects representing unlocked unspent outputs controlled by wallet keys enough to pay the target amount.\n\nArguments:\n1. targetamount (numeric, required) The minimum total output value of all returned inputs\n2. minamount (numeric, optional, default=0) The minimum amount output value of transaction output should have before it is considered\n3. minconf (numeric, optional, default=1) Minimum number of block confirmations required before a transaction output is considered\n4. account (string, optional, default=\"\") If set, only return unspent outputs from this account\n5. spendall (boolean, optional, default=false) If set, the target amount will be ignored and eligible inputs will be returned\n6. inputselectionmethod (string, optional, default=\"random\") Method to specify what inputs are returned\n7. seentxaddress (object, optional) Addresses or transaction hashes to be skipped when using UniqueTxInputSelection\n{\n \"Address or transaction hash\": Empty struct, (object) JSON object using addresses or transaction hashes as keys and empty structs as values to specify seen utxos\n ...\n}\n\nResult:\n{\n \"txid\": \"value\", (string) The transaction hash of the referenced output\n \"vout\": n, (numeric) The output index of the referenced output\n \"tree\": n, (numeric) The tree the transaction comes from\n \"txtype\": n, (numeric) The type of the transaction\n \"address\": \"value\", (string) The payment address that received the output\n \"account\": \"value\", (string) The account associated with the receiving payment address\n \"scriptPubKey\": \"value\", (string) The output script encoded as a hexadecimal string\n \"redeemScript\": \"value\", (string) The redeemScript if scriptPubKey is P2SH\n \"amount\": n.nnn, (numeric) The amount of the output valued in decred\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction\n \"spendable\": true|false, (boolean) Whether the output is entirely controlled by wallet keys/scripts (false for partially controlled multisig outputs or outputs to watch-only addresses)\n} \n", "lockaccount": "lockaccount \"account\"\n\nLock an individually-encrypted account\n\nArguments:\n1. account (string, required) Account to lock\n\nResult:\nNothing\n", "lockunspent": "lockunspent unlock [{\"amount\":n.nnn,\"txid\":\"value\",\"vout\":n,\"tree\":n},...]\n\nLocks or unlocks an unspent output.\nLocked outputs are not chosen for transaction inputs of authored transactions and are not included in 'listunspent' results.\nLocked outputs are volatile and are not saved across wallet restarts.\nIf unlock is true and no transaction outputs are specified, all locked outputs are marked unlocked.\n\nArguments:\n1. unlock (boolean, required) True to unlock outputs, false to lock\n2. transactions (array of object, required) Transaction outputs to lock or unlock\n[{\n \"amount\": n.nnn, (numeric) The the previous output amount\n \"txid\": \"value\", (string) The transaction hash of the referenced output\n \"vout\": n, (numeric) The output index of the referenced output\n \"tree\": n, (numeric) The tree to generate transaction for\n},...]\n\nResult:\ntrue|false (boolean) The boolean 'true'\n", "mixaccount": "mixaccount\n\nMix all outputs of an account.\n\nArguments:\nNone\n\nResult:\nNothing\n", @@ -112,4 +113,4 @@ var localeHelpDescs = map[string]func() map[string]string{ "en_US": helpDescsEnUS, } -var requestUsages = "abandontransaction \"hash\"\naccountaddressindex \"account\" branch\naccountsyncaddressindex \"account\" branch index\naccountunlocked \"account\"\naddmultisigaddress nrequired [\"key\",...] (\"account\")\naddtransaction \"blockhash\" \"transaction\"\nauditreuse (since)\nconsolidate inputs (\"account\" \"address\")\ncreatemultisig nrequired [\"key\",...]\ncreatenewaccount \"account\"\ncreaterawtransaction [{\"amount\":n.nnn,\"txid\":\"value\",\"vout\":n,\"tree\":n},...] {\"address\":amount,...} (locktime expiry)\ncreatesignature \"address\" inputindex hashtype \"previouspkscript\" \"serializedtransaction\"\ndisapprovepercent\ndiscoverusage (\"startblock\" discoveraccounts gaplimit)\ndumpprivkey \"address\"\nfundrawtransaction \"hexstring\" \"fundaccount\" ({\"changeaddress\":changeaddress,\"feerate\":feerate,\"conftarget\":conftarget})\ngeneratevote \"blockhash\" height \"tickethash\" votebits \"votebitsext\"\ngetaccount \"address\"\ngetaccountaddress \"account\"\ngetaddressesbyaccount \"account\"\ngetbalance (\"account\" minconf=1)\ngetbestblock\ngetbestblockhash\ngetblockcount\ngetblockhash index\ngetblock \"hash\" (verbose=true verbosetx=false)\ngetcoinjoinsbyacct\ngetinfo\ngetmasterpubkey (\"account\")\ngetmultisigoutinfo \"hash\" index\ngetnewaddress (\"account\" \"gappolicy\")\ngetpeerinfo\ngetrawchangeaddress (\"account\")\ngetreceivedbyaccount \"account\" (minconf=1)\ngetreceivedbyaddress \"address\" (minconf=1)\ngetstakeinfo\ngettickets includeimmature\ngettransaction \"txid\" (includewatchonly=false)\ngettxout \"txid\" vout tree (includemempool=true)\ngetunconfirmedbalance (\"account\")\ngetvotechoices (\"tickethash\")\ngetwalletfee\ngetcfilterv2 \"blockhash\"\nhelp (\"command\")\nimportcfiltersv2 startheight [\"filter\",...]\nimportprivkey \"privkey\" (\"label\" rescan=true scanfrom)\nimportscript \"hex\" (rescan=true scanfrom)\nimportxpub \"name\" \"xpub\"\nlistaccounts (minconf=1)\nlistaddresstransactions [\"address\",...] (\"account\")\nlistalltransactions (\"account\")\nlistlockunspent (\"account\")\nlistreceivedbyaccount (minconf=1 includeempty=false includewatchonly=false)\nlistreceivedbyaddress (minconf=1 includeempty=false includewatchonly=false)\nlistsinceblock (\"blockhash\" targetconfirmations=1 includewatchonly=false)\nlisttransactions (\"account\" count=10 from=0 includewatchonly=false)\nlistunspent (minconf=1 maxconf=9999999 [\"address\",...] \"account\")\nlockaccount \"account\"\nlockunspent unlock [{\"amount\":n.nnn,\"txid\":\"value\",\"vout\":n,\"tree\":n},...]\nmixaccount\nmixoutput \"outpoint\"\npurchaseticket \"fromaccount\" spendlimit (minconf=1 \"ticketaddress\" numtickets=1 \"pooladdress\" poolfees expiry \"comment\" dontsigntx)\nredeemmultisigout \"hash\" index tree (\"address\")\nredeemmultisigouts \"fromscraddress\" (\"toaddress\" number)\nrenameaccount \"oldaccount\" \"newaccount\"\nrescanwallet (beginheight=0)\nrevoketickets\nsendfrom \"fromaccount\" \"toaddress\" amount (minconf=1 \"comment\" \"commentto\")\nsendfromtreasury \"key\" amounts\nsendmany \"fromaccount\" {\"address\":amount,...} (minconf=1 \"comment\")\nsendrawtransaction \"hextx\" (allowhighfees=false)\nsendtoaddress \"address\" amount (\"comment\" \"commentto\")\nsendtomultisig \"fromaccount\" amount [\"pubkey\",...] (nrequired=1 minconf=1 \"comment\")\nsendtotreasury amount\nsetaccountpassphrase \"account\" \"passphrase\"\nsetdisapprovepercent percent\nsettreasurypolicy \"key\" \"policy\"\nsettspendpolicy \"hash\" \"policy\"\nsettxfee amount\nsetvotechoice \"agendaid\" \"choiceid\" (\"tickethash\")\nsignmessage \"address\" \"message\"\nsignrawtransaction \"rawtx\" ([{\"txid\":\"value\",\"vout\":n,\"tree\":n,\"scriptpubkey\":\"value\",\"redeemscript\":\"value\"},...] [\"privkey\",...] flags=\"ALL\")\nsignrawtransactions [\"rawtx\",...] (send=true)\nstakepooluserinfo \"user\"\nsweepaccount \"sourceaccount\" \"destinationaddress\" (requiredconfirmations feeperkb)\nsyncstatus\nticketinfo (startheight=0)\nticketsforaddress \"address\"\ntreasurypolicy (\"key\")\ntspendpolicy (\"hash\")\nunlockaccount \"account\" \"passphrase\"\nvalidateaddress \"address\"\nvalidatepredcp0005cf\nverifymessage \"address\" \"signature\" \"message\"\nversion\nwalletinfo\nwalletislocked\nwalletlock\nwalletpassphrase \"passphrase\" timeout\nwalletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\nwalletpubpassphrasechange \"oldpassphrase\" \"newpassphrase\"" +var requestUsages = "abandontransaction \"hash\"\naccountaddressindex \"account\" branch\naccountsyncaddressindex \"account\" branch index\naccountunlocked \"account\"\naddmultisigaddress nrequired [\"key\",...] (\"account\")\naddtransaction \"blockhash\" \"transaction\"\nauditreuse (since)\nconsolidate inputs (\"account\" \"address\")\ncreatemultisig nrequired [\"key\",...]\ncreatenewaccount \"account\"\ncreaterawtransaction [{\"amount\":n.nnn,\"txid\":\"value\",\"vout\":n,\"tree\":n},...] {\"address\":amount,...} (locktime expiry)\ncreatesignature \"address\" inputindex hashtype \"previouspkscript\" \"serializedtransaction\"\ndisapprovepercent\ndiscoverusage (\"startblock\" discoveraccounts gaplimit)\ndumpprivkey \"address\"\nfundrawtransaction \"hexstring\" \"fundaccount\" ({\"changeaddress\":changeaddress,\"feerate\":feerate,\"conftarget\":conftarget})\ngeneratevote \"blockhash\" height \"tickethash\" votebits \"votebitsext\"\ngetaccount \"address\"\ngetaccountaddress \"account\"\ngetaddressesbyaccount \"account\"\ngetbalance (\"account\" minconf=1)\ngetbestblock\ngetbestblockhash\ngetblockcount\ngetblockhash index\ngetblock \"hash\" (verbose=true verbosetx=false)\ngetcoinjoinsbyacct\ngetinfo\ngetmasterpubkey (\"account\")\ngetmultisigoutinfo \"hash\" index\ngetnewaddress (\"account\" \"gappolicy\")\ngetpeerinfo\ngetrawchangeaddress (\"account\")\ngetreceivedbyaccount \"account\" (minconf=1)\ngetreceivedbyaddress \"address\" (minconf=1)\ngetstakeinfo\ngettickets includeimmature\ngettransaction \"txid\" (includewatchonly=false)\ngettxout \"txid\" vout tree (includemempool=true)\ngetunconfirmedbalance (\"account\")\ngetvotechoices (\"tickethash\")\ngetwalletfee\ngetcfilterv2 \"blockhash\"\nhelp (\"command\")\nimportcfiltersv2 startheight [\"filter\",...]\nimportprivkey \"privkey\" (\"label\" rescan=true scanfrom)\nimportscript \"hex\" (rescan=true scanfrom)\nimportxpub \"name\" \"xpub\"\nlistaccounts (minconf=1)\nlistaddresstransactions [\"address\",...] (\"account\")\nlistalltransactions (\"account\")\nlistlockunspent (\"account\")\nlistreceivedbyaccount (minconf=1 includeempty=false includewatchonly=false)\nlistreceivedbyaddress (minconf=1 includeempty=false includewatchonly=false)\nlistsinceblock (\"blockhash\" targetconfirmations=1 includewatchonly=false)\nlisttransactions (\"account\" count=10 from=0 includewatchonly=false)\nlistunspent (minconf=1 maxconf=9999999 [\"address\",...] \"account\")\nselectunspent targetamount (minamount=0 minconf=1 account=\"\" spendall=false inputselectionmethod=\"random\" {\"address\":{},\"txhash\":{},...})\nlockaccount \"account\"\nlockunspent unlock [{\"amount\":n.nnn,\"txid\":\"value\",\"vout\":n,\"tree\":n},...]\nmixaccount\nmixoutput \"outpoint\"\npurchaseticket \"fromaccount\" spendlimit (minconf=1 \"ticketaddress\" numtickets=1 \"pooladdress\" poolfees expiry \"comment\" dontsigntx)\nredeemmultisigout \"hash\" index tree (\"address\")\nredeemmultisigouts \"fromscraddress\" (\"toaddress\" number)\nrenameaccount \"oldaccount\" \"newaccount\"\nrescanwallet (beginheight=0)\nrevoketickets\nsendfrom \"fromaccount\" \"toaddress\" amount (minconf=1 \"comment\" \"commentto\")\nsendfromtreasury \"key\" amounts\nsendmany \"fromaccount\" {\"address\":amount,...} (minconf=1 \"comment\")\nsendrawtransaction \"hextx\" (allowhighfees=false)\nsendtoaddress \"address\" amount (\"comment\" \"commentto\")\nsendtomultisig \"fromaccount\" amount [\"pubkey\",...] (nrequired=1 minconf=1 \"comment\")\nsendtotreasury amount\nsetaccountpassphrase \"account\" \"passphrase\"\nsetdisapprovepercent percent\nsettreasurypolicy \"key\" \"policy\"\nsettspendpolicy \"hash\" \"policy\"\nsettxfee amount\nsetvotechoice \"agendaid\" \"choiceid\" (\"tickethash\")\nsignmessage \"address\" \"message\"\nsignrawtransaction \"rawtx\" ([{\"txid\":\"value\",\"vout\":n,\"tree\":n,\"scriptpubkey\":\"value\",\"redeemscript\":\"value\"},...] [\"privkey\",...] flags=\"ALL\")\nsignrawtransactions [\"rawtx\",...] (send=true)\nstakepooluserinfo \"user\"\nsweepaccount \"sourceaccount\" \"destinationaddress\" (requiredconfirmations feeperkb)\nsyncstatus\nticketinfo (startheight=0)\nticketsforaddress \"address\"\ntreasurypolicy (\"key\")\ntspendpolicy (\"hash\")\nunlockaccount \"account\" \"passphrase\"\nvalidateaddress \"address\"\nvalidatepredcp0005cf\nverifymessage \"address\" \"signature\" \"message\"\nversion\nwalletinfo\nwalletislocked\nwalletlock\nwalletpassphrase \"passphrase\" timeout\nwalletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\nwalletpubpassphrasechange \"oldpassphrase\" \"newpassphrase\"" diff --git a/internal/rpchelp/helpdescs_en_US.go b/internal/rpchelp/helpdescs_en_US.go index 4e9585476..b92f58410 100644 --- a/internal/rpchelp/helpdescs_en_US.go +++ b/internal/rpchelp/helpdescs_en_US.go @@ -610,6 +610,19 @@ var helpDescsEnUS = map[string]string{ "listunspentresult-txtype": "The type of the transaction", "listunspentresult-tree": "The tree the transaction comes from", + // SelectUnspentCmd help. + "selectunspent--synopsis": "Returns a JSON array of objects representing unlocked unspent outputs controlled by wallet keys enough to pay the target amount.", + "selectunspent-targetamount": "The minimum total output value of all returned inputs", + "selectunspent-minamount": "The minimum amount output value of transaction output should have before it is considered", + "selectunspent-minconf": "Minimum number of block confirmations required before a transaction output is considered", + "selectunspent-account": "If set, only return unspent outputs from this account", + "selectunspent-spendall": "If set, the target amount will be ignored and eligible inputs will be returned", + "selectunspent-inputselectionmethod": "Method to specify what inputs are returned", + "selectunspent-seentxaddress": "Addresses or transaction hashes to be skipped when using UniqueTxInputSelection", + "selectunspent-seentxaddress--desc": "JSON object using addresses or transaction hashes as keys and empty structs as values to specify seen utxos", + "selectunspent-seentxaddress--key": "Address or transaction hash", + "selectunspent-seentxaddress--value": "Empty struct", + // LockAccountCmd help. "lockaccount--synopsis": "Lock an individually-encrypted account", "lockaccount-account": "Account to lock", diff --git a/rpc/client/dcrwallet/methods.go b/rpc/client/dcrwallet/methods.go index ee253df50..23bb426d8 100644 --- a/rpc/client/dcrwallet/methods.go +++ b/rpc/client/dcrwallet/methods.go @@ -94,10 +94,10 @@ func (c *Client) ListUnspentMinMaxAddresses(ctx context.Context, minConf, maxCon return res, err } -func (c *Client) SelectUnspent(ctx context.Context, minConf int, targetAmount, minAmount dcrutil.Amount, - accountName string, spendAll bool, inputSelectionMethod string, seenTxAddress map[string]struct{}) ([]types.ListUnspentResult, error) { +func (c *Client) SelectUnspent(ctx context.Context, targetAmount, minAmount dcrutil.Amount, minConf int, + account string, spendAll bool, inputSelectionMethod string, seenTxAddress map[string]struct{}) ([]types.ListUnspentResult, error) { var res []types.ListUnspentResult - err := c.Call(ctx, "selectunspent", &res, minConf, targetAmount.ToCoin(), minAmount.ToCoin(), accountName, spendAll, inputSelectionMethod, seenTxAddress) + err := c.Call(ctx, "selectunspent", &res, targetAmount.ToCoin(), minAmount.ToCoin(), minConf, account, spendAll, inputSelectionMethod, seenTxAddress) return res, err } diff --git a/rpc/jsonrpc/types/methods.go b/rpc/jsonrpc/types/methods.go index 5b9f19331..bb7f784da 100644 --- a/rpc/jsonrpc/types/methods.go +++ b/rpc/jsonrpc/types/methods.go @@ -660,22 +660,22 @@ func NewListUnspentCmd(minConf, maxConf *int, addresses *[]string) *ListUnspentC } type SelectUnspentCmd struct { - MinConf int TargetAmount float64 - MinAmount float64 - AccountName string - SpendAll bool - InputSelectionMethod string - SeenTxAddress *map[string]struct{} + MinAmount *float64 `jsonrpcdefault:"0"` + MinConf *int `jsonrpcdefault:"1"` + Account *string `jsonrpcdefault:"\"\""` + SpendAll *bool `jsonrpcdefault:"false"` + InputSelectionMethod *string `jsonrpcdefault:"\"random\""` + SeenTxAddress *map[string]struct{} `jsonrpcusage:"{\"address\":{},\"txhash\":{},...}"` } -func NewSelectUnspentCmd(minConf int, targetAmount, minAmount float64, accountName string, - spendAll bool, inputSelectionMethod string, seenTxAddress *map[string]struct{}) *SelectUnspentCmd { +func NewSelectUnspentCmd(targetAmount float64, minAmount *float64, minConf *int, account *string, + spendAll *bool, inputSelectionMethod *string, seenTxAddress *map[string]struct{}) *SelectUnspentCmd { return &SelectUnspentCmd{ - MinConf: minConf, TargetAmount: targetAmount, + MinConf: minConf, MinAmount: minAmount, - AccountName: accountName, + Account: account, SpendAll: spendAll, InputSelectionMethod: inputSelectionMethod, SeenTxAddress: seenTxAddress, diff --git a/rpc/jsonrpc/types/results.go b/rpc/jsonrpc/types/results.go index ff0ee85b0..43c81ef09 100644 --- a/rpc/jsonrpc/types/results.go +++ b/rpc/jsonrpc/types/results.go @@ -272,12 +272,12 @@ type InputSelectionMethod string const ( // RandomInputSelection indicates any random utxo can be selected. RandomInputSelection InputSelectionMethod = "random" - // RandomAddressInputSelection indicates utxos should be selected from - // one randomly selected address. + // RandomAddressInputSelection indicates that only utxos matching a randomly selected + // address should be selected. RandomAddressInputSelection InputSelectionMethod = "randomaddress" - // OneUTXOInputSelection indicates only one utxo should be selected. + // OneUTXOInputSelection indicates that only one utxo should be selected. OneUTXOInputSelection InputSelectionMethod = "oneutxo" - // UniqueTxInputSelection indicates only utxos with unique address + // UniqueTxInputSelection indicates that only utxos with unique address // and hash should be selected. UniqueTxInputSelection InputSelectionMethod = "uniquetx" ) From b595061dfaf42bf3cfbb673b88d5c407c0a0361a Mon Sep 17 00:00:00 2001 From: Olanrewaju Collins Date: Mon, 16 Aug 2021 16:50:39 +0100 Subject: [PATCH 4/7] Update comment and help description --- internal/rpc/jsonrpc/rpcserverhelp.go | 2 +- internal/rpchelp/helpdescs_en_US.go | 8 ++++---- wallet/wallet.go | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/internal/rpc/jsonrpc/rpcserverhelp.go b/internal/rpc/jsonrpc/rpcserverhelp.go index eaedd95fe..7f1c3238a 100644 --- a/internal/rpc/jsonrpc/rpcserverhelp.go +++ b/internal/rpc/jsonrpc/rpcserverhelp.go @@ -61,7 +61,7 @@ func helpDescsEnUS() map[string]string { "listsinceblock": "listsinceblock (\"blockhash\" targetconfirmations=1 includewatchonly=false)\n\nReturns a JSON array of objects listing details of all wallet transactions after some block.\n\nArguments:\n1. blockhash (string, optional) Hash of the parent block of the first block to consider transactions from, or unset to list all transactions\n2. targetconfirmations (numeric, optional, default=1) Minimum number of block confirmations of the last block in the result object. Must be 1 or greater. Note: The transactions array in the result object is not affected by this parameter\n3. includewatchonly (boolean, optional, default=false) Unused\n\nResult:\n{\n \"transactions\": [{ (array of object) JSON array of objects containing verbose details of the each transaction\n \"account\": \"value\", (string) DEPRECATED -- Unset\n \"address\": \"value\", (string) Payment address for a transaction output\n \"amount\": n.nnn, (numeric) The value of the transaction output valued in decred\n \"blockhash\": \"value\", (string) The hash of the block this transaction is mined in, or the empty string if unmined\n \"blockindex\": n, (numeric) Unset\n \"blocktime\": n, (numeric) The Unix time of the block header this transaction is mined in, or 0 if unmined\n \"category\": \"value\", (string) The kind of transaction: \"send\" for sent transactions, \"immature\" for immature coinbase outputs, \"generate\" for mature coinbase outputs, or \"recv\" for all other received outputs. Note: A single output may be included multiple times under different categories\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction\n \"fee\": n.nnn, (numeric) The total input value minus the total output value for sent transactions\n \"generated\": true|false, (boolean) Whether the transaction output is a coinbase output\n \"involveswatchonly\": true|false, (boolean) Unset\n \"time\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"timereceived\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"txid\": \"value\", (string) The hash of the transaction\n \"txtype\": \"value\", (string) The type of tx (regular tx, stake tx)\n \"vout\": n, (numeric) The transaction output index\n \"walletconflicts\": [\"value\",...], (array of string) Unset\n \"comment\": \"value\", (string) Unset\n \"otheraccount\": \"value\", (string) Unset\n },...], \n \"lastblock\": \"value\", (string) Hash of the latest-synced block to be used in later calls to listsinceblock\n} \n", "listtransactions": "listtransactions (\"account\" count=10 from=0 includewatchonly=false)\n\nReturns a JSON array of objects containing verbose details for wallet transactions.\n\nArguments:\n1. account (string, optional) DEPRECATED -- Unused (must be unset or \"*\")\n2. count (numeric, optional, default=10) Maximum number of transactions to create results from\n3. from (numeric, optional, default=0) Number of transactions to skip before results are created\n4. includewatchonly (boolean, optional, default=false) Unused\n\nResult:\n[{\n \"account\": \"value\", (string) DEPRECATED -- Unset\n \"address\": \"value\", (string) Payment address for a transaction output\n \"amount\": n.nnn, (numeric) The value of the transaction output valued in decred\n \"blockhash\": \"value\", (string) The hash of the block this transaction is mined in, or the empty string if unmined\n \"blockindex\": n, (numeric) Unset\n \"blocktime\": n, (numeric) The Unix time of the block header this transaction is mined in, or 0 if unmined\n \"category\": \"value\", (string) The kind of transaction: \"send\" for sent transactions, \"immature\" for immature coinbase outputs, \"generate\" for mature coinbase outputs, or \"recv\" for all other received outputs. Note: A single output may be included multiple times under different categories\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction\n \"fee\": n.nnn, (numeric) The total input value minus the total output value for sent transactions\n \"generated\": true|false, (boolean) Whether the transaction output is a coinbase output\n \"involveswatchonly\": true|false, (boolean) Unset\n \"time\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"timereceived\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"txid\": \"value\", (string) The hash of the transaction\n \"txtype\": \"value\", (string) The type of tx (regular tx, stake tx)\n \"vout\": n, (numeric) The transaction output index\n \"walletconflicts\": [\"value\",...], (array of string) Unset\n \"comment\": \"value\", (string) Unset\n \"otheraccount\": \"value\", (string) Unset\n},...]\n", "listunspent": "listunspent (minconf=1 maxconf=9999999 [\"address\",...] \"account\")\n\nReturns a JSON array of objects representing unlocked unspent outputs controlled by wallet keys.\n\nArguments:\n1. minconf (numeric, optional, default=1) Minimum number of block confirmations required before a transaction output is considered\n2. maxconf (numeric, optional, default=9999999) Maximum number of block confirmations required before a transaction output is excluded\n3. addresses (array of string, optional) If set, limits the returned details to unspent outputs received by any of these payment addresses\n4. account (string, optional) If set, only return unspent outputs from this account\n\nResult:\n{\n \"txid\": \"value\", (string) The transaction hash of the referenced output\n \"vout\": n, (numeric) The output index of the referenced output\n \"tree\": n, (numeric) The tree the transaction comes from\n \"txtype\": n, (numeric) The type of the transaction\n \"address\": \"value\", (string) The payment address that received the output\n \"account\": \"value\", (string) The account associated with the receiving payment address\n \"scriptPubKey\": \"value\", (string) The output script encoded as a hexadecimal string\n \"redeemScript\": \"value\", (string) The redeemScript if scriptPubKey is P2SH\n \"amount\": n.nnn, (numeric) The amount of the output valued in decred\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction\n \"spendable\": true|false, (boolean) Whether the output is entirely controlled by wallet keys/scripts (false for partially controlled multisig outputs or outputs to watch-only addresses)\n} \n", - "selectunspent": "selectunspent targetamount (minamount=0 minconf=1 account=\"\" spendall=false inputselectionmethod=\"random\" {\"address\":{},\"txhash\":{},...})\n\nReturns a JSON array of objects representing unlocked unspent outputs controlled by wallet keys enough to pay the target amount.\n\nArguments:\n1. targetamount (numeric, required) The minimum total output value of all returned inputs\n2. minamount (numeric, optional, default=0) The minimum amount output value of transaction output should have before it is considered\n3. minconf (numeric, optional, default=1) Minimum number of block confirmations required before a transaction output is considered\n4. account (string, optional, default=\"\") If set, only return unspent outputs from this account\n5. spendall (boolean, optional, default=false) If set, the target amount will be ignored and eligible inputs will be returned\n6. inputselectionmethod (string, optional, default=\"random\") Method to specify what inputs are returned\n7. seentxaddress (object, optional) Addresses or transaction hashes to be skipped when using UniqueTxInputSelection\n{\n \"Address or transaction hash\": Empty struct, (object) JSON object using addresses or transaction hashes as keys and empty structs as values to specify seen utxos\n ...\n}\n\nResult:\n{\n \"txid\": \"value\", (string) The transaction hash of the referenced output\n \"vout\": n, (numeric) The output index of the referenced output\n \"tree\": n, (numeric) The tree the transaction comes from\n \"txtype\": n, (numeric) The type of the transaction\n \"address\": \"value\", (string) The payment address that received the output\n \"account\": \"value\", (string) The account associated with the receiving payment address\n \"scriptPubKey\": \"value\", (string) The output script encoded as a hexadecimal string\n \"redeemScript\": \"value\", (string) The redeemScript if scriptPubKey is P2SH\n \"amount\": n.nnn, (numeric) The amount of the output valued in decred\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction\n \"spendable\": true|false, (boolean) Whether the output is entirely controlled by wallet keys/scripts (false for partially controlled multisig outputs or outputs to watch-only addresses)\n} \n", + "selectunspent": "selectunspent targetamount (minamount=0 minconf=1 account=\"\" spendall=false inputselectionmethod=\"random\" {\"address\":{},\"txhash\":{},...})\n\nReturns a JSON array of objects representing unlocked unspent outputs controlled by wallet keys that are enough to pay target amount.\n\nArguments:\n1. targetamount (numeric, required) The minimum total output value of all returned inputs\n2. minamount (numeric, optional, default=0) The minimum amount output value of transaction output should have before it is considered\n3. minconf (numeric, optional, default=1) Minimum block confirmations required for a utxo to be considered\n4. account (string, optional, default=\"\") If set, only return unspent outputs from this account\n5. spendall (boolean, optional, default=false) If set, all eligible inputs will be returned. (target amount will be ignored)\n6. inputselectionmethod (string, optional, default=\"random\") The method for how transaction inputs should be selected.\n7. seentxaddress (object, optional) Addresses or transaction hashes to be skipped when using UniqueTxInputSelection\n{\n \"Address or transaction hash\": Empty struct, (object) JSON object using addresses or transaction hashes as keys and empty structs as values to specify seen utxos\n ...\n}\n\nResult:\n{\n \"txid\": \"value\", (string) The transaction hash of the referenced output\n \"vout\": n, (numeric) The output index of the referenced output\n \"tree\": n, (numeric) The tree the transaction comes from\n \"txtype\": n, (numeric) The type of the transaction\n \"address\": \"value\", (string) The payment address that received the output\n \"account\": \"value\", (string) The account associated with the receiving payment address\n \"scriptPubKey\": \"value\", (string) The output script encoded as a hexadecimal string\n \"redeemScript\": \"value\", (string) The redeemScript if scriptPubKey is P2SH\n \"amount\": n.nnn, (numeric) The amount of the output valued in decred\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction\n \"spendable\": true|false, (boolean) Whether the output is entirely controlled by wallet keys/scripts (false for partially controlled multisig outputs or outputs to watch-only addresses)\n} \n", "lockaccount": "lockaccount \"account\"\n\nLock an individually-encrypted account\n\nArguments:\n1. account (string, required) Account to lock\n\nResult:\nNothing\n", "lockunspent": "lockunspent unlock [{\"amount\":n.nnn,\"txid\":\"value\",\"vout\":n,\"tree\":n},...]\n\nLocks or unlocks an unspent output.\nLocked outputs are not chosen for transaction inputs of authored transactions and are not included in 'listunspent' results.\nLocked outputs are volatile and are not saved across wallet restarts.\nIf unlock is true and no transaction outputs are specified, all locked outputs are marked unlocked.\n\nArguments:\n1. unlock (boolean, required) True to unlock outputs, false to lock\n2. transactions (array of object, required) Transaction outputs to lock or unlock\n[{\n \"amount\": n.nnn, (numeric) The the previous output amount\n \"txid\": \"value\", (string) The transaction hash of the referenced output\n \"vout\": n, (numeric) The output index of the referenced output\n \"tree\": n, (numeric) The tree to generate transaction for\n},...]\n\nResult:\ntrue|false (boolean) The boolean 'true'\n", "mixaccount": "mixaccount\n\nMix all outputs of an account.\n\nArguments:\nNone\n\nResult:\nNothing\n", diff --git a/internal/rpchelp/helpdescs_en_US.go b/internal/rpchelp/helpdescs_en_US.go index b92f58410..26ad57b33 100644 --- a/internal/rpchelp/helpdescs_en_US.go +++ b/internal/rpchelp/helpdescs_en_US.go @@ -611,13 +611,13 @@ var helpDescsEnUS = map[string]string{ "listunspentresult-tree": "The tree the transaction comes from", // SelectUnspentCmd help. - "selectunspent--synopsis": "Returns a JSON array of objects representing unlocked unspent outputs controlled by wallet keys enough to pay the target amount.", + "selectunspent--synopsis": "Returns a JSON array of objects representing unlocked unspent outputs controlled by wallet keys that are enough to pay target amount.", "selectunspent-targetamount": "The minimum total output value of all returned inputs", "selectunspent-minamount": "The minimum amount output value of transaction output should have before it is considered", - "selectunspent-minconf": "Minimum number of block confirmations required before a transaction output is considered", + "selectunspent-minconf": "Minimum block confirmations required for a utxo to be considered", "selectunspent-account": "If set, only return unspent outputs from this account", - "selectunspent-spendall": "If set, the target amount will be ignored and eligible inputs will be returned", - "selectunspent-inputselectionmethod": "Method to specify what inputs are returned", + "selectunspent-spendall": "If set, all eligible inputs will be returned. (target amount will be ignored)", + "selectunspent-inputselectionmethod": "The method for how transaction inputs should be selected.", "selectunspent-seentxaddress": "Addresses or transaction hashes to be skipped when using UniqueTxInputSelection", "selectunspent-seentxaddress--desc": "JSON object using addresses or transaction hashes as keys and empty structs as values to specify seen utxos", "selectunspent-seentxaddress--key": "Address or transaction hash", diff --git a/wallet/wallet.go b/wallet/wallet.go index 26e101fae..d0a5773d9 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -3718,12 +3718,12 @@ func (w *Wallet) ListUnspent(ctx context.Context, minconf, maxconf int32, addres } // SelectUnspent returns a slice of objects representing the unspent wallet -// transactions fitting the given criteria and enough to pay the target amount. -// The transaction amount and confirmations will be more than the amount & minconf -// parameter, only transactions matching the accountName will be returned if it's -// not empty and the targetAmount is ignored if spendAll is true. The -// inputSelectionMethod method determines how and what inputs should be selected -// and the seenTxIDs can only be used with UniqueTxInputSelection to determine what +// transactions for the given criteria that are enough to pay the target amount. +// The transaction amount and confirmations will be greater than the amount +// & minconf parameters. Only utxos matching the accountName will be returned if +// that parameter is used. targetAmount is ignored if spendAll is set to true. The +// inputSelectionMethod determines how inputs should be selected and the +// seenTxIDs is for use with the UniqueTxInputSelection parameter to determine what // transaction hash or address should be skipped. func (w *Wallet) SelectUnspent(ctx context.Context, targetAmount, minAmount dcrutil.Amount, minconf int32, accountName string, spendAll bool, seenTxIDs map[string]struct{}, inputSelectionMethod types.InputSelectionMethod) ([]*types.ListUnspentResult, error) { From 5b469ac433be26d8a7a7deb426478c628bc35c1c Mon Sep 17 00:00:00 2001 From: Olanrewaju Collins Date: Mon, 30 Aug 2021 04:00:15 +0100 Subject: [PATCH 5/7] Rename parameter SeenTxAddress -> SkipTxAddress - Shuffle transactions using wallet package's shuffle() and remove redundant sort. - Change log level to trace --- internal/rpc/jsonrpc/methods.go | 12 +++---- internal/rpc/jsonrpc/rpcserverhelp.go | 4 +-- internal/rpchelp/helpdescs_en_US.go | 10 +++--- rpc/client/dcrwallet/methods.go | 4 +-- rpc/jsonrpc/types/methods.go | 30 ++++++++--------- wallet/wallet.go | 47 +++++++++++++-------------- 6 files changed, 52 insertions(+), 55 deletions(-) diff --git a/internal/rpc/jsonrpc/methods.go b/internal/rpc/jsonrpc/methods.go index c021b3b29..2337c7b86 100644 --- a/internal/rpc/jsonrpc/methods.go +++ b/internal/rpc/jsonrpc/methods.go @@ -3125,17 +3125,17 @@ func (s *Server) selectUnspent(ctx context.Context, icmd interface{}) (interface } var inputMethod = types.RandomInputSelection - if cmd.InputSelectionMethod != nil { - inputMethod = types.InputSelectionMethod(*cmd.InputSelectionMethod) + if cmd != nil { + inputMethod = types.InputSelectionMethod(*cmd.InputMethod) } - seenTxAddress := make(map[string]struct{}) - if cmd.SeenTxAddress != nil { - seenTxAddress = *cmd.SeenTxAddress + skipTxAddress := make(map[string]struct{}) + if cmd.SkipTxAddress != nil { + skipTxAddress = *cmd.SkipTxAddress } result, err := w.SelectUnspent(ctx, targetAmount, minAmount, int32(*cmd.MinConf), account, - spendAll, seenTxAddress, inputMethod) + spendAll, skipTxAddress, inputMethod) if err != nil { return nil, err } diff --git a/internal/rpc/jsonrpc/rpcserverhelp.go b/internal/rpc/jsonrpc/rpcserverhelp.go index 7f1c3238a..1730b73f5 100644 --- a/internal/rpc/jsonrpc/rpcserverhelp.go +++ b/internal/rpc/jsonrpc/rpcserverhelp.go @@ -61,7 +61,7 @@ func helpDescsEnUS() map[string]string { "listsinceblock": "listsinceblock (\"blockhash\" targetconfirmations=1 includewatchonly=false)\n\nReturns a JSON array of objects listing details of all wallet transactions after some block.\n\nArguments:\n1. blockhash (string, optional) Hash of the parent block of the first block to consider transactions from, or unset to list all transactions\n2. targetconfirmations (numeric, optional, default=1) Minimum number of block confirmations of the last block in the result object. Must be 1 or greater. Note: The transactions array in the result object is not affected by this parameter\n3. includewatchonly (boolean, optional, default=false) Unused\n\nResult:\n{\n \"transactions\": [{ (array of object) JSON array of objects containing verbose details of the each transaction\n \"account\": \"value\", (string) DEPRECATED -- Unset\n \"address\": \"value\", (string) Payment address for a transaction output\n \"amount\": n.nnn, (numeric) The value of the transaction output valued in decred\n \"blockhash\": \"value\", (string) The hash of the block this transaction is mined in, or the empty string if unmined\n \"blockindex\": n, (numeric) Unset\n \"blocktime\": n, (numeric) The Unix time of the block header this transaction is mined in, or 0 if unmined\n \"category\": \"value\", (string) The kind of transaction: \"send\" for sent transactions, \"immature\" for immature coinbase outputs, \"generate\" for mature coinbase outputs, or \"recv\" for all other received outputs. Note: A single output may be included multiple times under different categories\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction\n \"fee\": n.nnn, (numeric) The total input value minus the total output value for sent transactions\n \"generated\": true|false, (boolean) Whether the transaction output is a coinbase output\n \"involveswatchonly\": true|false, (boolean) Unset\n \"time\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"timereceived\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"txid\": \"value\", (string) The hash of the transaction\n \"txtype\": \"value\", (string) The type of tx (regular tx, stake tx)\n \"vout\": n, (numeric) The transaction output index\n \"walletconflicts\": [\"value\",...], (array of string) Unset\n \"comment\": \"value\", (string) Unset\n \"otheraccount\": \"value\", (string) Unset\n },...], \n \"lastblock\": \"value\", (string) Hash of the latest-synced block to be used in later calls to listsinceblock\n} \n", "listtransactions": "listtransactions (\"account\" count=10 from=0 includewatchonly=false)\n\nReturns a JSON array of objects containing verbose details for wallet transactions.\n\nArguments:\n1. account (string, optional) DEPRECATED -- Unused (must be unset or \"*\")\n2. count (numeric, optional, default=10) Maximum number of transactions to create results from\n3. from (numeric, optional, default=0) Number of transactions to skip before results are created\n4. includewatchonly (boolean, optional, default=false) Unused\n\nResult:\n[{\n \"account\": \"value\", (string) DEPRECATED -- Unset\n \"address\": \"value\", (string) Payment address for a transaction output\n \"amount\": n.nnn, (numeric) The value of the transaction output valued in decred\n \"blockhash\": \"value\", (string) The hash of the block this transaction is mined in, or the empty string if unmined\n \"blockindex\": n, (numeric) Unset\n \"blocktime\": n, (numeric) The Unix time of the block header this transaction is mined in, or 0 if unmined\n \"category\": \"value\", (string) The kind of transaction: \"send\" for sent transactions, \"immature\" for immature coinbase outputs, \"generate\" for mature coinbase outputs, or \"recv\" for all other received outputs. Note: A single output may be included multiple times under different categories\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction\n \"fee\": n.nnn, (numeric) The total input value minus the total output value for sent transactions\n \"generated\": true|false, (boolean) Whether the transaction output is a coinbase output\n \"involveswatchonly\": true|false, (boolean) Unset\n \"time\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"timereceived\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"txid\": \"value\", (string) The hash of the transaction\n \"txtype\": \"value\", (string) The type of tx (regular tx, stake tx)\n \"vout\": n, (numeric) The transaction output index\n \"walletconflicts\": [\"value\",...], (array of string) Unset\n \"comment\": \"value\", (string) Unset\n \"otheraccount\": \"value\", (string) Unset\n},...]\n", "listunspent": "listunspent (minconf=1 maxconf=9999999 [\"address\",...] \"account\")\n\nReturns a JSON array of objects representing unlocked unspent outputs controlled by wallet keys.\n\nArguments:\n1. minconf (numeric, optional, default=1) Minimum number of block confirmations required before a transaction output is considered\n2. maxconf (numeric, optional, default=9999999) Maximum number of block confirmations required before a transaction output is excluded\n3. addresses (array of string, optional) If set, limits the returned details to unspent outputs received by any of these payment addresses\n4. account (string, optional) If set, only return unspent outputs from this account\n\nResult:\n{\n \"txid\": \"value\", (string) The transaction hash of the referenced output\n \"vout\": n, (numeric) The output index of the referenced output\n \"tree\": n, (numeric) The tree the transaction comes from\n \"txtype\": n, (numeric) The type of the transaction\n \"address\": \"value\", (string) The payment address that received the output\n \"account\": \"value\", (string) The account associated with the receiving payment address\n \"scriptPubKey\": \"value\", (string) The output script encoded as a hexadecimal string\n \"redeemScript\": \"value\", (string) The redeemScript if scriptPubKey is P2SH\n \"amount\": n.nnn, (numeric) The amount of the output valued in decred\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction\n \"spendable\": true|false, (boolean) Whether the output is entirely controlled by wallet keys/scripts (false for partially controlled multisig outputs or outputs to watch-only addresses)\n} \n", - "selectunspent": "selectunspent targetamount (minamount=0 minconf=1 account=\"\" spendall=false inputselectionmethod=\"random\" {\"address\":{},\"txhash\":{},...})\n\nReturns a JSON array of objects representing unlocked unspent outputs controlled by wallet keys that are enough to pay target amount.\n\nArguments:\n1. targetamount (numeric, required) The minimum total output value of all returned inputs\n2. minamount (numeric, optional, default=0) The minimum amount output value of transaction output should have before it is considered\n3. minconf (numeric, optional, default=1) Minimum block confirmations required for a utxo to be considered\n4. account (string, optional, default=\"\") If set, only return unspent outputs from this account\n5. spendall (boolean, optional, default=false) If set, all eligible inputs will be returned. (target amount will be ignored)\n6. inputselectionmethod (string, optional, default=\"random\") The method for how transaction inputs should be selected.\n7. seentxaddress (object, optional) Addresses or transaction hashes to be skipped when using UniqueTxInputSelection\n{\n \"Address or transaction hash\": Empty struct, (object) JSON object using addresses or transaction hashes as keys and empty structs as values to specify seen utxos\n ...\n}\n\nResult:\n{\n \"txid\": \"value\", (string) The transaction hash of the referenced output\n \"vout\": n, (numeric) The output index of the referenced output\n \"tree\": n, (numeric) The tree the transaction comes from\n \"txtype\": n, (numeric) The type of the transaction\n \"address\": \"value\", (string) The payment address that received the output\n \"account\": \"value\", (string) The account associated with the receiving payment address\n \"scriptPubKey\": \"value\", (string) The output script encoded as a hexadecimal string\n \"redeemScript\": \"value\", (string) The redeemScript if scriptPubKey is P2SH\n \"amount\": n.nnn, (numeric) The amount of the output valued in decred\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction\n \"spendable\": true|false, (boolean) Whether the output is entirely controlled by wallet keys/scripts (false for partially controlled multisig outputs or outputs to watch-only addresses)\n} \n", + "selectunspent": "selectunspent targetamount (minamount=0 minconf=1 account=\"\" spendall=false inputmethod=\"random\" {\"address\":{},\"txhash\":{},...})\n\nReturns a JSON array of objects representing unlocked unspent outputs controlled by wallet keys that are enough to pay target amount.\n\nArguments:\n1. targetamount (numeric, required) The minimum total output value of all returned inputs\n2. minamount (numeric, optional, default=0) The minimum amount output value of transaction output should have before it is considered\n3. minconf (numeric, optional, default=1) Minimum block confirmations required for a utxo to be considered\n4. account (string, optional, default=\"\") If set, only return unspent outputs from this account\n5. spendall (boolean, optional, default=false) If set, all eligible inputs will be returned. (target amount will be ignored)\n6. inputmethod (string, optional, default=\"random\") The method for how transaction inputs should be selected.\n7. skiptxaddress (object, optional) Addresses or transaction hashes to be skipped when using UniqueTxInputSelection\n{\n \"Address or transaction hash\": Empty struct, (object) JSON object using addresses or transaction hashes as keys and empty structs as values to specify seen utxos\n ...\n}\n\nResult:\n{\n \"txid\": \"value\", (string) The transaction hash of the referenced output\n \"vout\": n, (numeric) The output index of the referenced output\n \"tree\": n, (numeric) The tree the transaction comes from\n \"txtype\": n, (numeric) The type of the transaction\n \"address\": \"value\", (string) The payment address that received the output\n \"account\": \"value\", (string) The account associated with the receiving payment address\n \"scriptPubKey\": \"value\", (string) The output script encoded as a hexadecimal string\n \"redeemScript\": \"value\", (string) The redeemScript if scriptPubKey is P2SH\n \"amount\": n.nnn, (numeric) The amount of the output valued in decred\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction\n \"spendable\": true|false, (boolean) Whether the output is entirely controlled by wallet keys/scripts (false for partially controlled multisig outputs or outputs to watch-only addresses)\n} \n", "lockaccount": "lockaccount \"account\"\n\nLock an individually-encrypted account\n\nArguments:\n1. account (string, required) Account to lock\n\nResult:\nNothing\n", "lockunspent": "lockunspent unlock [{\"amount\":n.nnn,\"txid\":\"value\",\"vout\":n,\"tree\":n},...]\n\nLocks or unlocks an unspent output.\nLocked outputs are not chosen for transaction inputs of authored transactions and are not included in 'listunspent' results.\nLocked outputs are volatile and are not saved across wallet restarts.\nIf unlock is true and no transaction outputs are specified, all locked outputs are marked unlocked.\n\nArguments:\n1. unlock (boolean, required) True to unlock outputs, false to lock\n2. transactions (array of object, required) Transaction outputs to lock or unlock\n[{\n \"amount\": n.nnn, (numeric) The the previous output amount\n \"txid\": \"value\", (string) The transaction hash of the referenced output\n \"vout\": n, (numeric) The output index of the referenced output\n \"tree\": n, (numeric) The tree to generate transaction for\n},...]\n\nResult:\ntrue|false (boolean) The boolean 'true'\n", "mixaccount": "mixaccount\n\nMix all outputs of an account.\n\nArguments:\nNone\n\nResult:\nNothing\n", @@ -113,4 +113,4 @@ var localeHelpDescs = map[string]func() map[string]string{ "en_US": helpDescsEnUS, } -var requestUsages = "abandontransaction \"hash\"\naccountaddressindex \"account\" branch\naccountsyncaddressindex \"account\" branch index\naccountunlocked \"account\"\naddmultisigaddress nrequired [\"key\",...] (\"account\")\naddtransaction \"blockhash\" \"transaction\"\nauditreuse (since)\nconsolidate inputs (\"account\" \"address\")\ncreatemultisig nrequired [\"key\",...]\ncreatenewaccount \"account\"\ncreaterawtransaction [{\"amount\":n.nnn,\"txid\":\"value\",\"vout\":n,\"tree\":n},...] {\"address\":amount,...} (locktime expiry)\ncreatesignature \"address\" inputindex hashtype \"previouspkscript\" \"serializedtransaction\"\ndisapprovepercent\ndiscoverusage (\"startblock\" discoveraccounts gaplimit)\ndumpprivkey \"address\"\nfundrawtransaction \"hexstring\" \"fundaccount\" ({\"changeaddress\":changeaddress,\"feerate\":feerate,\"conftarget\":conftarget})\ngeneratevote \"blockhash\" height \"tickethash\" votebits \"votebitsext\"\ngetaccount \"address\"\ngetaccountaddress \"account\"\ngetaddressesbyaccount \"account\"\ngetbalance (\"account\" minconf=1)\ngetbestblock\ngetbestblockhash\ngetblockcount\ngetblockhash index\ngetblock \"hash\" (verbose=true verbosetx=false)\ngetcoinjoinsbyacct\ngetinfo\ngetmasterpubkey (\"account\")\ngetmultisigoutinfo \"hash\" index\ngetnewaddress (\"account\" \"gappolicy\")\ngetpeerinfo\ngetrawchangeaddress (\"account\")\ngetreceivedbyaccount \"account\" (minconf=1)\ngetreceivedbyaddress \"address\" (minconf=1)\ngetstakeinfo\ngettickets includeimmature\ngettransaction \"txid\" (includewatchonly=false)\ngettxout \"txid\" vout tree (includemempool=true)\ngetunconfirmedbalance (\"account\")\ngetvotechoices (\"tickethash\")\ngetwalletfee\ngetcfilterv2 \"blockhash\"\nhelp (\"command\")\nimportcfiltersv2 startheight [\"filter\",...]\nimportprivkey \"privkey\" (\"label\" rescan=true scanfrom)\nimportscript \"hex\" (rescan=true scanfrom)\nimportxpub \"name\" \"xpub\"\nlistaccounts (minconf=1)\nlistaddresstransactions [\"address\",...] (\"account\")\nlistalltransactions (\"account\")\nlistlockunspent (\"account\")\nlistreceivedbyaccount (minconf=1 includeempty=false includewatchonly=false)\nlistreceivedbyaddress (minconf=1 includeempty=false includewatchonly=false)\nlistsinceblock (\"blockhash\" targetconfirmations=1 includewatchonly=false)\nlisttransactions (\"account\" count=10 from=0 includewatchonly=false)\nlistunspent (minconf=1 maxconf=9999999 [\"address\",...] \"account\")\nselectunspent targetamount (minamount=0 minconf=1 account=\"\" spendall=false inputselectionmethod=\"random\" {\"address\":{},\"txhash\":{},...})\nlockaccount \"account\"\nlockunspent unlock [{\"amount\":n.nnn,\"txid\":\"value\",\"vout\":n,\"tree\":n},...]\nmixaccount\nmixoutput \"outpoint\"\npurchaseticket \"fromaccount\" spendlimit (minconf=1 \"ticketaddress\" numtickets=1 \"pooladdress\" poolfees expiry \"comment\" dontsigntx)\nredeemmultisigout \"hash\" index tree (\"address\")\nredeemmultisigouts \"fromscraddress\" (\"toaddress\" number)\nrenameaccount \"oldaccount\" \"newaccount\"\nrescanwallet (beginheight=0)\nrevoketickets\nsendfrom \"fromaccount\" \"toaddress\" amount (minconf=1 \"comment\" \"commentto\")\nsendfromtreasury \"key\" amounts\nsendmany \"fromaccount\" {\"address\":amount,...} (minconf=1 \"comment\")\nsendrawtransaction \"hextx\" (allowhighfees=false)\nsendtoaddress \"address\" amount (\"comment\" \"commentto\")\nsendtomultisig \"fromaccount\" amount [\"pubkey\",...] (nrequired=1 minconf=1 \"comment\")\nsendtotreasury amount\nsetaccountpassphrase \"account\" \"passphrase\"\nsetdisapprovepercent percent\nsettreasurypolicy \"key\" \"policy\"\nsettspendpolicy \"hash\" \"policy\"\nsettxfee amount\nsetvotechoice \"agendaid\" \"choiceid\" (\"tickethash\")\nsignmessage \"address\" \"message\"\nsignrawtransaction \"rawtx\" ([{\"txid\":\"value\",\"vout\":n,\"tree\":n,\"scriptpubkey\":\"value\",\"redeemscript\":\"value\"},...] [\"privkey\",...] flags=\"ALL\")\nsignrawtransactions [\"rawtx\",...] (send=true)\nstakepooluserinfo \"user\"\nsweepaccount \"sourceaccount\" \"destinationaddress\" (requiredconfirmations feeperkb)\nsyncstatus\nticketinfo (startheight=0)\nticketsforaddress \"address\"\ntreasurypolicy (\"key\")\ntspendpolicy (\"hash\")\nunlockaccount \"account\" \"passphrase\"\nvalidateaddress \"address\"\nvalidatepredcp0005cf\nverifymessage \"address\" \"signature\" \"message\"\nversion\nwalletinfo\nwalletislocked\nwalletlock\nwalletpassphrase \"passphrase\" timeout\nwalletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\nwalletpubpassphrasechange \"oldpassphrase\" \"newpassphrase\"" +var requestUsages = "abandontransaction \"hash\"\naccountaddressindex \"account\" branch\naccountsyncaddressindex \"account\" branch index\naccountunlocked \"account\"\naddmultisigaddress nrequired [\"key\",...] (\"account\")\naddtransaction \"blockhash\" \"transaction\"\nauditreuse (since)\nconsolidate inputs (\"account\" \"address\")\ncreatemultisig nrequired [\"key\",...]\ncreatenewaccount \"account\"\ncreaterawtransaction [{\"amount\":n.nnn,\"txid\":\"value\",\"vout\":n,\"tree\":n},...] {\"address\":amount,...} (locktime expiry)\ncreatesignature \"address\" inputindex hashtype \"previouspkscript\" \"serializedtransaction\"\ndisapprovepercent\ndiscoverusage (\"startblock\" discoveraccounts gaplimit)\ndumpprivkey \"address\"\nfundrawtransaction \"hexstring\" \"fundaccount\" ({\"changeaddress\":changeaddress,\"feerate\":feerate,\"conftarget\":conftarget})\ngeneratevote \"blockhash\" height \"tickethash\" votebits \"votebitsext\"\ngetaccount \"address\"\ngetaccountaddress \"account\"\ngetaddressesbyaccount \"account\"\ngetbalance (\"account\" minconf=1)\ngetbestblock\ngetbestblockhash\ngetblockcount\ngetblockhash index\ngetblock \"hash\" (verbose=true verbosetx=false)\ngetcoinjoinsbyacct\ngetinfo\ngetmasterpubkey (\"account\")\ngetmultisigoutinfo \"hash\" index\ngetnewaddress (\"account\" \"gappolicy\")\ngetpeerinfo\ngetrawchangeaddress (\"account\")\ngetreceivedbyaccount \"account\" (minconf=1)\ngetreceivedbyaddress \"address\" (minconf=1)\ngetstakeinfo\ngettickets includeimmature\ngettransaction \"txid\" (includewatchonly=false)\ngettxout \"txid\" vout tree (includemempool=true)\ngetunconfirmedbalance (\"account\")\ngetvotechoices (\"tickethash\")\ngetwalletfee\ngetcfilterv2 \"blockhash\"\nhelp (\"command\")\nimportcfiltersv2 startheight [\"filter\",...]\nimportprivkey \"privkey\" (\"label\" rescan=true scanfrom)\nimportscript \"hex\" (rescan=true scanfrom)\nimportxpub \"name\" \"xpub\"\nlistaccounts (minconf=1)\nlistaddresstransactions [\"address\",...] (\"account\")\nlistalltransactions (\"account\")\nlistlockunspent (\"account\")\nlistreceivedbyaccount (minconf=1 includeempty=false includewatchonly=false)\nlistreceivedbyaddress (minconf=1 includeempty=false includewatchonly=false)\nlistsinceblock (\"blockhash\" targetconfirmations=1 includewatchonly=false)\nlisttransactions (\"account\" count=10 from=0 includewatchonly=false)\nlistunspent (minconf=1 maxconf=9999999 [\"address\",...] \"account\")\nselectunspent targetamount (minamount=0 minconf=1 account=\"\" spendall=false inputmethod=\"random\" {\"address\":{},\"txhash\":{},...})\nlockaccount \"account\"\nlockunspent unlock [{\"amount\":n.nnn,\"txid\":\"value\",\"vout\":n,\"tree\":n},...]\nmixaccount\nmixoutput \"outpoint\"\npurchaseticket \"fromaccount\" spendlimit (minconf=1 \"ticketaddress\" numtickets=1 \"pooladdress\" poolfees expiry \"comment\" dontsigntx)\nredeemmultisigout \"hash\" index tree (\"address\")\nredeemmultisigouts \"fromscraddress\" (\"toaddress\" number)\nrenameaccount \"oldaccount\" \"newaccount\"\nrescanwallet (beginheight=0)\nrevoketickets\nsendfrom \"fromaccount\" \"toaddress\" amount (minconf=1 \"comment\" \"commentto\")\nsendfromtreasury \"key\" amounts\nsendmany \"fromaccount\" {\"address\":amount,...} (minconf=1 \"comment\")\nsendrawtransaction \"hextx\" (allowhighfees=false)\nsendtoaddress \"address\" amount (\"comment\" \"commentto\")\nsendtomultisig \"fromaccount\" amount [\"pubkey\",...] (nrequired=1 minconf=1 \"comment\")\nsendtotreasury amount\nsetaccountpassphrase \"account\" \"passphrase\"\nsetdisapprovepercent percent\nsettreasurypolicy \"key\" \"policy\"\nsettspendpolicy \"hash\" \"policy\"\nsettxfee amount\nsetvotechoice \"agendaid\" \"choiceid\" (\"tickethash\")\nsignmessage \"address\" \"message\"\nsignrawtransaction \"rawtx\" ([{\"txid\":\"value\",\"vout\":n,\"tree\":n,\"scriptpubkey\":\"value\",\"redeemscript\":\"value\"},...] [\"privkey\",...] flags=\"ALL\")\nsignrawtransactions [\"rawtx\",...] (send=true)\nstakepooluserinfo \"user\"\nsweepaccount \"sourceaccount\" \"destinationaddress\" (requiredconfirmations feeperkb)\nsyncstatus\nticketinfo (startheight=0)\nticketsforaddress \"address\"\ntreasurypolicy (\"key\")\ntspendpolicy (\"hash\")\nunlockaccount \"account\" \"passphrase\"\nvalidateaddress \"address\"\nvalidatepredcp0005cf\nverifymessage \"address\" \"signature\" \"message\"\nversion\nwalletinfo\nwalletislocked\nwalletlock\nwalletpassphrase \"passphrase\" timeout\nwalletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\nwalletpubpassphrasechange \"oldpassphrase\" \"newpassphrase\"" diff --git a/internal/rpchelp/helpdescs_en_US.go b/internal/rpchelp/helpdescs_en_US.go index 26ad57b33..7173dc60a 100644 --- a/internal/rpchelp/helpdescs_en_US.go +++ b/internal/rpchelp/helpdescs_en_US.go @@ -617,11 +617,11 @@ var helpDescsEnUS = map[string]string{ "selectunspent-minconf": "Minimum block confirmations required for a utxo to be considered", "selectunspent-account": "If set, only return unspent outputs from this account", "selectunspent-spendall": "If set, all eligible inputs will be returned. (target amount will be ignored)", - "selectunspent-inputselectionmethod": "The method for how transaction inputs should be selected.", - "selectunspent-seentxaddress": "Addresses or transaction hashes to be skipped when using UniqueTxInputSelection", - "selectunspent-seentxaddress--desc": "JSON object using addresses or transaction hashes as keys and empty structs as values to specify seen utxos", - "selectunspent-seentxaddress--key": "Address or transaction hash", - "selectunspent-seentxaddress--value": "Empty struct", + "selectunspent-inputmethod": "The method for how transaction inputs should be selected.", + "selectunspent-skiptxaddress": "Addresses or transaction hashes to be skipped when using UniqueTxInputSelection", + "selectunspent-skiptxaddress--desc": "JSON object using addresses or transaction hashes as keys and empty structs as values to specify seen utxos", + "selectunspent-skiptxaddress--key": "Address or transaction hash", + "selectunspent-skiptxaddress--value": "Empty struct", // LockAccountCmd help. "lockaccount--synopsis": "Lock an individually-encrypted account", diff --git a/rpc/client/dcrwallet/methods.go b/rpc/client/dcrwallet/methods.go index 23bb426d8..af5c3415c 100644 --- a/rpc/client/dcrwallet/methods.go +++ b/rpc/client/dcrwallet/methods.go @@ -95,9 +95,9 @@ func (c *Client) ListUnspentMinMaxAddresses(ctx context.Context, minConf, maxCon } func (c *Client) SelectUnspent(ctx context.Context, targetAmount, minAmount dcrutil.Amount, minConf int, - account string, spendAll bool, inputSelectionMethod string, seenTxAddress map[string]struct{}) ([]types.ListUnspentResult, error) { + account string, spendAll bool, inputMethod string, skiptxaddress map[string]struct{}) ([]types.ListUnspentResult, error) { var res []types.ListUnspentResult - err := c.Call(ctx, "selectunspent", &res, targetAmount.ToCoin(), minAmount.ToCoin(), minConf, account, spendAll, inputSelectionMethod, seenTxAddress) + err := c.Call(ctx, "selectunspent", &res, targetAmount.ToCoin(), minAmount.ToCoin(), minConf, account, spendAll, inputMethod, skiptxaddress) return res, err } diff --git a/rpc/jsonrpc/types/methods.go b/rpc/jsonrpc/types/methods.go index bb7f784da..b20b4db64 100644 --- a/rpc/jsonrpc/types/methods.go +++ b/rpc/jsonrpc/types/methods.go @@ -660,25 +660,25 @@ func NewListUnspentCmd(minConf, maxConf *int, addresses *[]string) *ListUnspentC } type SelectUnspentCmd struct { - TargetAmount float64 - MinAmount *float64 `jsonrpcdefault:"0"` - MinConf *int `jsonrpcdefault:"1"` - Account *string `jsonrpcdefault:"\"\""` - SpendAll *bool `jsonrpcdefault:"false"` - InputSelectionMethod *string `jsonrpcdefault:"\"random\""` - SeenTxAddress *map[string]struct{} `jsonrpcusage:"{\"address\":{},\"txhash\":{},...}"` + TargetAmount float64 + MinAmount *float64 `jsonrpcdefault:"0"` + MinConf *int `jsonrpcdefault:"1"` + Account *string `jsonrpcdefault:"\"\""` + SpendAll *bool `jsonrpcdefault:"false"` + InputMethod *string `jsonrpcdefault:"\"random\""` + SkipTxAddress *map[string]struct{} `jsonrpcusage:"{\"address\":{},\"txhash\":{},...}"` } func NewSelectUnspentCmd(targetAmount float64, minAmount *float64, minConf *int, account *string, - spendAll *bool, inputSelectionMethod *string, seenTxAddress *map[string]struct{}) *SelectUnspentCmd { + spendAll *bool, inputMethod *string, skipTxAddress *map[string]struct{}) *SelectUnspentCmd { return &SelectUnspentCmd{ - TargetAmount: targetAmount, - MinConf: minConf, - MinAmount: minAmount, - Account: account, - SpendAll: spendAll, - InputSelectionMethod: inputSelectionMethod, - SeenTxAddress: seenTxAddress, + TargetAmount: targetAmount, + MinConf: minConf, + MinAmount: minAmount, + Account: account, + SpendAll: spendAll, + InputMethod: inputMethod, + SkipTxAddress: skipTxAddress, } } diff --git a/wallet/wallet.go b/wallet/wallet.go index d0a5773d9..942171526 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -11,7 +11,6 @@ import ( "encoding/binary" "encoding/hex" "fmt" - "math/rand" "runtime" "sort" "strconv" @@ -3719,14 +3718,14 @@ func (w *Wallet) ListUnspent(ctx context.Context, minconf, maxconf int32, addres // SelectUnspent returns a slice of objects representing the unspent wallet // transactions for the given criteria that are enough to pay the target amount. -// The transaction amount and confirmations will be greater than the amount +// The output amount and confirmations will be greater than the amount // & minconf parameters. Only utxos matching the accountName will be returned if // that parameter is used. targetAmount is ignored if spendAll is set to true. The -// inputSelectionMethod determines how inputs should be selected and the -// seenTxIDs is for use with the UniqueTxInputSelection parameter to determine what -// transaction hash or address should be skipped. +// inputMethod determines how inputs should be selected and theseenTxIDs is for +// use with the UniqueTxInputSelection parameter to determine what transaction hash +// or address should be skipped. func (w *Wallet) SelectUnspent(ctx context.Context, targetAmount, minAmount dcrutil.Amount, minconf int32, accountName string, - spendAll bool, seenTxIDs map[string]struct{}, inputSelectionMethod types.InputSelectionMethod) ([]*types.ListUnspentResult, error) { + spendAll bool, skipTxAddress map[string]struct{}, inputMethod types.InputSelectionMethod) ([]*types.ListUnspentResult, error) { const op errors.Op = "wallet.SelectUnspent" var ( @@ -3744,7 +3743,6 @@ func (w *Wallet) SelectUnspent(ctx context.Context, targetAmount, minAmount dcru if err != nil { return err } - sort.Sort(sort.Reverse(creditSlice(unspent))) defaultAccountName, err := w.manager.AccountName( addrmgrNs, udb.DefaultAccountNum) @@ -3753,8 +3751,7 @@ func (w *Wallet) SelectUnspent(ctx context.Context, targetAmount, minAmount dcru } // Shuffe utxos - rand.Seed(time.Now().UnixNano()) - rand.Shuffle(len(unspent), func(i, j int) { unspent[i], unspent[j] = unspent[j], unspent[i] }) + shuffle(len(unspent), func(i, j int) { unspent[i], unspent[j] = unspent[j], unspent[i] }) // used for RandomAddressInputSelection var randomUnspent *types.ListUnspentResult @@ -3763,11 +3760,11 @@ func (w *Wallet) SelectUnspent(ctx context.Context, targetAmount, minAmount dcru output := unspent[i] if output.Amount < minAmount { - log.Infof("Skipping utxo %s, amount: %s, min: %s", output.Hash.String(), output.Amount, minAmount) + log.Tracef("Skipping utxo %s, amount: %s, min: %s", output.Hash.String(), output.Amount, minAmount) continue } - if inputSelectionMethod == types.OneUTXOInputSelection { + if inputMethod == types.OneUTXOInputSelection { // We're selecting only one utxo so this loop will run until we find // a single utxo that can pay the target amount. @@ -3775,11 +3772,11 @@ func (w *Wallet) SelectUnspent(ctx context.Context, targetAmount, minAmount dcru // continue if this utxo cannot pay the require amount continue } - } else if inputSelectionMethod == types.UniqueTxInputSelection { + } else if inputMethod == types.UniqueTxInputSelection { // skip duplicate tx id - _, seenTxID := seenTxIDs[output.Hash.String()] - if seenTxID { - log.Infof("Skipping duplicate txid: %s:%d", output.Hash.String(), output.Index) + _, skipTx := skipTxAddress[output.Hash.String()] + if skipTx { + log.Tracef("Skipping duplicate txid: %s:%d", output.Hash.String(), output.Index) continue } } @@ -3858,11 +3855,11 @@ func (w *Wallet) SelectUnspent(ctx context.Context, targetAmount, minAmount dcru } } - if inputSelectionMethod == types.UniqueTxInputSelection { + if inputMethod == types.UniqueTxInputSelection { // skip duplicate address - _, seenAddress := seenTxIDs[addrs[0].String()] - if seenAddress { - log.Info("Skipping duplicate address:", addrs[0].String()) + _, skipAddress := skipTxAddress[addrs[0].String()] + if skipAddress { + log.Trace("Skipping duplicate address:", addrs[0].String()) continue } } @@ -3950,18 +3947,18 @@ func (w *Wallet) SelectUnspent(ctx context.Context, targetAmount, minAmount dcru if len(addrs) > 0 { result.Address = addrs[0].String() - seenTxIDs[result.Address] = struct{}{} + skipTxAddress[result.Address] = struct{}{} } results = append(results, result) currentTotal += output.Amount - seenTxIDs[result.TxID] = struct{}{} + skipTxAddress[result.TxID] = struct{}{} - if inputSelectionMethod == types.RandomAddressInputSelection && randomUnspent == nil { + if inputMethod == types.RandomAddressInputSelection && randomUnspent == nil { randomUnspent = result } - if inputSelectionMethod == types.OneUTXOInputSelection { + if inputMethod == types.OneUTXOInputSelection { return nil } else if currentTotal >= targetAmount { if spendAll { @@ -3972,9 +3969,9 @@ func (w *Wallet) SelectUnspent(ctx context.Context, targetAmount, minAmount dcru } } - if inputSelectionMethod == types.RandomAddressInputSelection { + if inputMethod == types.RandomAddressInputSelection { return fmt.Errorf("insufficient balance, selected address does not have enough utxos to pay %s", targetAmount) - } else if inputSelectionMethod == types.OneUTXOInputSelection { + } else if inputMethod == types.OneUTXOInputSelection { return fmt.Errorf("insufficient balance, no utxo is available to pay %s", targetAmount) } From c19b0a9df7e26bc2fbd107be80399c595f544613 Mon Sep 17 00:00:00 2001 From: Olanrewaju Collins Date: Mon, 30 Aug 2021 04:10:28 +0100 Subject: [PATCH 6/7] Skip only address not exist error --- wallet/wallet.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/wallet/wallet.go b/wallet/wallet.go index 942171526..a0e875d18 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -3845,15 +3845,17 @@ func (w *Wallet) SelectUnspent(ctx context.Context, targetAmount, minAmount dcru continue } if len(addrs) > 0 { - acct, err := w.manager.AddrAccount( - addrmgrNs, addrs[0]) + acct, err := w.manager.AddrAccount(addrmgrNs, addrs[0]) if err == nil { - s, err := w.manager.AccountName( - addrmgrNs, acct) + var s string + s, err = w.manager.AccountName(addrmgrNs, acct) if err == nil { acctName = s } } + if err != nil && !errors.Is(err, errors.NotExist) { + return err + } if inputMethod == types.UniqueTxInputSelection { // skip duplicate address From 0301450b5760d9a3d98da4541a8731c89e8cafa1 Mon Sep 17 00:00:00 2001 From: Olanrewaju Collins Date: Mon, 30 Aug 2021 15:26:53 +0100 Subject: [PATCH 7/7] Exclude ticket commitments and check for OP_TGEN maturity --- wallet/wallet.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/wallet/wallet.go b/wallet/wallet.go index a0e875d18..9ac0a5c50 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -3801,11 +3801,9 @@ func (w *Wallet) SelectUnspent(ctx context.Context, targetAmount, minAmount dcru switch details.TxRecord.TxType { case stake.TxTypeSStx: - // Ticket commitment, only spendable after ticket maturity. + // Ticket commitment, not spendable by regular transactions. if output.Index == 0 { - if !ticketMatured(w.chainParams, details.Height(), tipHeight) { - continue - } + continue } // Change outputs. if (output.Index > 0) && (output.Index%2 == 0) { @@ -3816,12 +3814,14 @@ func (w *Wallet) SelectUnspent(ctx context.Context, targetAmount, minAmount dcru case stake.TxTypeSSGen: // All non-OP_RETURN outputs for SSGen tx are only spendable // after coinbase maturity many blocks. - if !coinbaseMatured(w.chainParams, details.Height(), tipHeight) { - continue - } + fallthrough case stake.TxTypeSSRtx: // All outputs for SSRtx tx are only spendable // after coinbase maturity many blocks. + fallthrough + case stake.TxTypeTSpend: + // All non-OP_RETURN outputs for TGen tx are only spendable + // after coinbase maturity many blocks. if !coinbaseMatured(w.chainParams, details.Height(), tipHeight) { continue }