diff --git a/docs/json_rpc_api.mediawiki b/docs/json_rpc_api.mediawiki index f545a37ae3..8c1a1784f2 100644 --- a/docs/json_rpc_api.mediawiki +++ b/docs/json_rpc_api.mediawiki @@ -272,9 +272,9 @@ the method name for further details such as parameter and return information. |Y |Returns a mix message and its message type by its hash if it is accepted by and currently in the mixpool. |- -|[[#getmixpairrequests|getmixpairrequests]] +|[[#getmixpoolinfo|getmixpoolinfo]] |Y -|Returns the current set of mixing pair request messages from the mixpool. WARNING: This is experimental and will very likely be removed in the next version. Do not use! +|Returns the current state of the mixpool including timing of the next mix epoch and pending pair requests. |- |[[#getnettotals|getnettotals]] |Y @@ -1549,24 +1549,50 @@ of the best block. ---- -====getmixpairrequests==== +====getmixpoolinfo==== {| !Method -|getmixpairrequests +|getmixpoolinfo |- !Parameters |None |- !Description | -: Returns the current set of mixing pair request messages from the mixpool. -: WARNING: This is experimental and will very likely be removed in the next version. Do not use! +: Returns the current state of the mixpool including timing of the next mix epoch and pending pair requests. |- !Returns -|(json array of string) hex-encoded mixing pair request messages. +|(json object) +: epoch: (numeric) Duration between mix epochs, in seconds. +: nextepoch: (numeric) Unix timestamp of the next mix epoch. +: pairings: (json array of object) Pending pair requests grouped by mixing compatibility. +:: mixamount: (numeric) Amount of each mixed output, in DCR. +:: scriptclass: (string) Script class of the mixed outputs. +:: txversion: (numeric) Transaction version of the mix transaction. +:: locktime: (numeric) Lock time of the mix transaction. +:: pairingflags: (numeric) Pairing flags of the pair request. +:: pairrequests: (json array of object) The pair requests matching these mixing parameters. +::: hash: (string) Hash of the pair request message. +::: identity: (string) Participant ephemeral public key identity as a hex string. +::: messagecount: (numeric) Number of mixed outputs, each of value mixamount, the pair request is creating. +::: inputvalue: (numeric) Total value of inputs contributed by the pair request, in DCR. +::: utxos: (json array of object) Unspent transaction outputs contributed by the pair request. +:::: txid: (string) The hash of the origin transaction. +:::: vout: (numeric) The index of the output being contributed. +:::: tree: (numeric) The tree of the output being contributed. +:::: amountin: (numeric) The amount of the output, in DCR. +:::: blockheight: (numeric) The height of the block containing the origin transaction. +:::: scriptPubKey: (json object) The public key script used to pay coins. +::::: asm: (string) Disassembly of the script. +::::: hex: (string) Hex-encoded bytes of the script. +::::: reqSigs: (numeric) The number of required signatures. +::::: type: (string) The type of the script (e.g. 'pubkeyhash'). +::::: addresses: (json array of string) The Decred addresses associated with this output. +::::: version: (numeric) The script version. +::: expiry: (numeric) Block height at which the pair request expires. |- !Example Return -|["a1d1d8c6ffc20a51ad9a5aa093c31bdeb06a0f627af95...",...] +|{"epoch":600,"nextepoch":1751049600,"pairings":[{"mixamount":1,"scriptclass":"P2PKH-secp256k1-v0","txversion":1,"locktime":0,"pairingflags":1,"pairrequests":[{"hash":"a1d1d8c6...","identity":"02d1a...","messagecount":1,"inputvalue":1.2,"utxos":[{"txid":"a1d1d8c6...","vout":0,"tree":0,"amountin":1.2,"blockheight":987000,"scriptPubKey":{"asm":"OP_DUP OP_HASH160 2da3aaa402b110247f08c3ea2300a0567de77a5b OP_EQUALVERIFY OP_CHECKSIG","hex":"76a9142da3aaa402b110247f08c3ea2300a0567de77a5b88ac","reqSigs":1,"type":"pubkeyhash","addresses":["DsV8DxpEUFyZtcAwpWfejmWRpR5FEsrM6Yo"],"version":0}}],"expiry":987654}]}]} |} ---- diff --git a/internal/rpcserver/interface.go b/internal/rpcserver/interface.go index 64c9443b84..0db36e6ce6 100644 --- a/internal/rpcserver/interface.go +++ b/internal/rpcserver/interface.go @@ -665,6 +665,9 @@ type MixPooler interface { // Message searches the mixing pool for a message by its hash. Message(query *chainhash.Hash) (mixing.Message, error) + + // Epoch returns the duration between mix epochs. + Epoch() time.Duration } // TxIndexer provides an interface for retrieving details for a given diff --git a/internal/rpcserver/rpcserver.go b/internal/rpcserver/rpcserver.go index 5a3013bc18..e7fdf97067 100644 --- a/internal/rpcserver/rpcserver.go +++ b/internal/rpcserver/rpcserver.go @@ -62,8 +62,8 @@ import ( // API version constants. const ( - jsonrpcSemverMajor = 8 - jsonrpcSemverMinor = 3 + jsonrpcSemverMajor = 9 + jsonrpcSemverMinor = 0 jsonrpcSemverPatch = 0 ) @@ -210,7 +210,7 @@ var rpcHandlersBeforeInit = map[types.Method]commandHandler{ "getmempoolinfo": handleGetMempoolInfo, "getmininginfo": handleGetMiningInfo, "getmixmessage": handleGetMixMessage, - "getmixpairrequests": handleGetMixPairRequests, + "getmixpoolinfo": handleGetMixpoolInfo, "getnettotals": handleGetNetTotals, "getnetworkhashps": handleGetNetworkHashPS, "getnetworkinfo": handleGetNetworkInfo, @@ -380,7 +380,7 @@ var rpcLimited = map[string]struct{}{ "getheaders": {}, "getinfo": {}, "getmixmessage": {}, - "getmixpairrequests": {}, + "getmixpoolinfo": {}, "getnettotals": {}, "getnetworkhashps": {}, "getnetworkinfo": {}, @@ -1266,6 +1266,38 @@ func createVinList(mtx *wire.MsgTx, isTreasuryEnabled bool) []types.Vin { return vinList } +// createScriptPubKeyResult returns a JSON object describing the passed public +// key script information. +func createScriptPubKeyResult(scriptVersion uint16, pkScript []byte, + chainParams *chaincfg.Params) types.ScriptPubKeyResult { + + // Disassemble script into single line printable format. The disassembled + // string will contain [error] inline if the script doesn't fully parse, so + // ignore the error here. + script := pkScript + disbuf, _ := txscript.DisasmString(script) + + // Attempt to extract known addresses associated with the script. + scriptType, addrs := stdscript.ExtractAddrs(scriptVersion, script, + chainParams) + addresses := make([]string, len(addrs)) + for i, addr := range addrs { + addresses[i] = addr.String() + } + + // Determine the number of required signatures for known standard types. + reqSigs := stdscript.DetermineRequiredSigs(scriptVersion, script) + + return types.ScriptPubKeyResult{ + Asm: disbuf, + Hex: hex.EncodeToString(script), + ReqSigs: int32(reqSigs), + Type: scriptType.String(), + Addresses: addresses, + Version: scriptVersion, + } +} + // createVoutList returns a slice of JSON objects for the outputs of the passed // transaction. func createVoutList(mtx *wire.MsgTx, chainParams *chaincfg.Params, @@ -2666,27 +2698,82 @@ func handleGetMixMessage(_ context.Context, s *Server, cmd any) (any, error) { return &result, nil } -// handleGetMixPairRequests implements the getmixpairrequests command, -// returning all current mixing pair requests messages from mixpool. -func handleGetMixPairRequests(_ context.Context, s *Server, _ any) (any, error) { +// handleGetMixpoolInfo implements the getmixpoolinfo command, returning the +// timing of the next mix epoch and the pending pair requests grouped by pairing +// description. +func handleGetMixpoolInfo(_ context.Context, s *Server, _ any) (any, error) { mp := s.cfg.MixPooler - prs := mp.MixPRs() - buf := new(strings.Builder) - res := make([]string, 0, len(prs)) - - const pver = wire.MixVersion + // Use a map to group PRs by their pairing description. This is converted to + // a slice for JSON marshalling later. + groups := make(map[string]*types.Pairing) for _, pr := range prs { - err := pr.BtcEncode(hex.NewEncoder(buf), pver) + pairing, err := pr.Pairing() if err != nil { - return nil, err + return nil, rpcInternalErr(err, "Failed to generate PR pairing") } - res = append(res, buf.String()) - buf.Reset() + + key := string(pairing) + group, ok := groups[key] + if !ok { + group = &types.Pairing{ + MixAmount: dcrutil.Amount(pr.MixAmount).ToCoin(), + ScriptClass: pr.ScriptClass, + TxVersion: pr.TxVersion, + LockTime: pr.LockTime, + PairingFlags: pr.PairingFlags, + PairRequests: make([]types.PairRequest, 0, min(len(prs), mixing.MaxPeers)), + } + groups[key] = group + } + + // The pair request only provides the hash, index and tree of the UTXO, + // so amountIn, blockHeight and scriptPubKey are retrieved from the + // local UTXO set. + utxos := make([]types.PairRequestUTXO, len(pr.UTXOs)) + for i := range pr.UTXOs { + op := pr.UTXOs[i].OutPoint + + entry, err := s.cfg.Chain.FetchUtxoEntry(op) + if entry == nil || err != nil { + return nil, rpcNoTxInfoError(&op.Hash) + } + + utxos[i].Txid = op.Hash.String() + utxos[i].Vout = op.Index + utxos[i].Tree = op.Tree + utxos[i].AmountIn = dcrutil.Amount(entry.Amount()).ToCoin() + utxos[i].BlockHeight = uint32(entry.BlockHeight()) + utxos[i].ScriptPubKey = createScriptPubKeyResult( + entry.ScriptVersion(), entry.PkScript(), s.cfg.ChainParams) + } + + group.PairRequests = append(group.PairRequests, types.PairRequest{ + Hash: pr.Hash().String(), + Identity: hex.EncodeToString(pr.Identity[:]), + MessageCount: pr.MessageCount, + InputValue: dcrutil.Amount(pr.InputValue).ToCoin(), + UTXOs: utxos, + Expiry: pr.Expiry, + }) } - return res, nil + // Convert map to slice for JSON marshalling. + pairings := make([]types.Pairing, 0, len(groups)) + for _, group := range groups { + pairings = append(pairings, *group) + } + + epoch := mp.Epoch() + nextEpoch := s.cfg.Clock.Now().Truncate(epoch).Add(epoch) + + result := types.GetMixpoolInfoResult{ + Epoch: int64(epoch.Seconds()), + NextEpoch: nextEpoch.Unix(), + Pairings: pairings, + } + return &result, nil } // handleGetNetTotals implements the getnettotals command. @@ -3752,35 +3839,12 @@ func handleGetTxOut(_ context.Context, s *Server, cmd any) (any, error) { isCoinbase = entry.IsCoinBase() } - // Disassemble script into single line printable format. The - // disassembled string will contain [error] inline if the script - // doesn't fully parse, so ignore the error here. - script := pkScript - disbuf, _ := txscript.DisasmString(script) - - // Attempt to extract known addresses associated with the script. - scriptType, addrs := stdscript.ExtractAddrs(scriptVersion, script, - s.cfg.ChainParams) - addresses := make([]string, len(addrs)) - for i, addr := range addrs { - addresses[i] = addr.String() - } - - // Determine the number of required signatures for known standard types. - reqSigs := stdscript.DetermineRequiredSigs(scriptVersion, script) - txOutReply := &types.GetTxOutResult{ BestBlock: bestBlockHash, Confirmations: confirmations, Value: dcrutil.Amount(value).ToUnit(dcrutil.AmountCoin), - ScriptPubKey: types.ScriptPubKeyResult{ - Asm: disbuf, - Hex: hex.EncodeToString(pkScript), - ReqSigs: int32(reqSigs), - Type: scriptType.String(), - Addresses: addresses, - Version: scriptVersion, - }, + ScriptPubKey: createScriptPubKeyResult(scriptVersion, pkScript, + s.cfg.ChainParams), Coinbase: isCoinbase, } return txOutReply, nil diff --git a/internal/rpcserver/rpcserverhandlers_test.go b/internal/rpcserver/rpcserverhandlers_test.go index fbd8716fee..e26e90b197 100644 --- a/internal/rpcserver/rpcserverhandlers_test.go +++ b/internal/rpcserver/rpcserverhandlers_test.go @@ -1184,6 +1184,7 @@ type testMixPooler struct { mixPRs []*wire.MsgMixPairReq message mixing.Message messageErr error + epoch time.Duration } // MixPRs returns a mocked slice of MixPR messages. @@ -1196,6 +1197,11 @@ func (mp *testMixPooler) Message(query *chainhash.Hash) (mixing.Message, error) return mp.message, mp.messageErr } +// Epoch returns a mocked duration between mix epochs. +func (mp *testMixPooler) Epoch() time.Duration { + return mp.epoch +} + // testNtfnManager provides a mock notification manager by implementing the // NtfnManager interface. type testNtfnManager struct { @@ -4606,6 +4612,188 @@ func TestHandleGetMempoolInfo(t *testing.T) { }}) } +func TestHandleGetMixpoolInfo(t *testing.T) { + t.Parallel() + + epoch := 10 * time.Minute + now := time.Unix(1700000000, 0) + + // Construct the mock UtxoEntry which will be returned by FetchUtxoEntry. + script := hexToBytes("76a9142da3aaa402b110247f08c3ea2300a0567de77a5b88ac") + const scriptVersion = uint16(0) + mockUtxo := &testRPCUtxoEntry{ + amount: 500000000, + height: 12345, + pkScript: script, + scriptVersion: scriptVersion, + } + + // Construct the representation of the script expected to be returned by the + // RPC. + disbuf, _ := txscript.DisasmString(script) + scriptType, addrs := stdscript.ExtractAddrs(scriptVersion, script, + defaultChainParams) + addresses := make([]string, len(addrs)) + for i, addr := range addrs { + addresses[i] = addr.String() + } + reqSigs := stdscript.DetermineRequiredSigs(scriptVersion, script) + scriptPubKey := types.ScriptPubKeyResult{ + Asm: disbuf, + Hex: hex.EncodeToString(script), + ReqSigs: int32(reqSigs), + Type: scriptType.String(), + Addresses: addresses, + Version: scriptVersion, + } + + // Construct two UTXOs for use in the pair requests, and the representation + // of them expected to be returned by the RPC. + utxo1 := wire.OutPoint{Hash: chainhash.Hash{0x01}, Index: 2, Tree: 0} + utxo1Result := types.PairRequestUTXO{ + Txid: "0000000000000000000000000000000000000000000000000000000000000001", + Vout: 2, + Tree: 0, + AmountIn: 5.00, + BlockHeight: 12345, + ScriptPubKey: scriptPubKey, + } + + utxo2 := wire.OutPoint{Hash: chainhash.Hash{0x02}, Index: 3, Tree: 1} + utxo2Result := types.PairRequestUTXO{ + Txid: "0000000000000000000000000000000000000000000000000000000000000002", + Vout: 3, + Tree: 1, + AmountIn: 5.00, + BlockHeight: 12345, + ScriptPubKey: scriptPubKey, + } + + // Construct two pair requests each contributing a distinct UTXO, and + // sharing the same pairing description so they are grouped together. + pr1 := &wire.MsgMixPairReq{ + Identity: [33]byte{0x02, 0xaa}, + Expiry: 100, + MixAmount: 1000000, + ScriptClass: string(mixing.ScriptClassP2PKHv0), + TxVersion: 1, + MessageCount: 2, + InputValue: 2500000, + UTXOs: []wire.MixPairReqUTXO{{OutPoint: utxo1}}, + PairingFlags: 2, + } + pr2 := &wire.MsgMixPairReq{ + Identity: [33]byte{0x03, 0xbb}, + Expiry: 200, + MixAmount: 1000000, + ScriptClass: string(mixing.ScriptClassP2PKHv0), + TxVersion: 1, + MessageCount: 1, + InputValue: 1500000, + UTXOs: []wire.MixPairReqUTXO{{OutPoint: utxo2}}, + PairingFlags: 2, + } + + hasher := blake256.NewHasher256() + pr1.WriteHash(hasher) + pr2.WriteHash(hasher) + + testRPCServerHandler(t, []rpcTest{{ + name: "handleGetMixpoolInfo: no pending pair requests", + handler: handleGetMixpoolInfo, + cmd: &types.GetMixpoolInfoCmd{}, + mockClock: &testClock{now: now}, + mockMixPooler: func() *testMixPooler { + mp := defaultMockMixPooler() + mp.epoch = epoch + return mp + }(), + result: &types.GetMixpoolInfoResult{ + Epoch: 600, // 10 minutes + NextEpoch: 1700000400, + Pairings: []types.Pairing{}, + }, + }, { + name: "handleGetMixpoolInfo: pending pair requests grouped by pairing", + handler: handleGetMixpoolInfo, + cmd: &types.GetMixpoolInfoCmd{}, + mockClock: &testClock{now: now}, + mockChain: func() *testRPCChain { + chain := defaultMockRPCChain() + chain.fetchUtxoEntry = mockUtxo + return chain + }(), + mockMixPooler: func() *testMixPooler { + mp := defaultMockMixPooler() + mp.epoch = epoch + mp.mixPRs = []*wire.MsgMixPairReq{pr1, pr2} + return mp + }(), + result: &types.GetMixpoolInfoResult{ + Epoch: 600, // 10 minutes + NextEpoch: 1700000400, + Pairings: []types.Pairing{{ + MixAmount: 0.01, + ScriptClass: string(mixing.ScriptClassP2PKHv0), + TxVersion: 1, + LockTime: 0, + PairingFlags: 2, + PairRequests: []types.PairRequest{{ + Hash: pr1.Hash().String(), + Identity: hex.EncodeToString(pr1.Identity[:]), + MessageCount: 2, + InputValue: 0.025, + UTXOs: []types.PairRequestUTXO{utxo1Result}, + Expiry: 100, + }, { + Hash: pr2.Hash().String(), + Identity: hex.EncodeToString(pr2.Identity[:]), + MessageCount: 1, + InputValue: 0.015, + UTXOs: []types.PairRequestUTXO{utxo2Result}, + Expiry: 200, + }}, + }}, + }, + }, { + name: "handleGetMixpoolInfo: utxo not found in the utxo set", + handler: handleGetMixpoolInfo, + cmd: &types.GetMixpoolInfoCmd{}, + mockClock: &testClock{now: now}, + mockChain: func() *testRPCChain { + chain := defaultMockRPCChain() + chain.fetchUtxoEntry = nil + return chain + }(), + mockMixPooler: func() *testMixPooler { + mp := defaultMockMixPooler() + mp.epoch = epoch + mp.mixPRs = []*wire.MsgMixPairReq{pr1} + return mp + }(), + wantErr: true, + errCode: dcrjson.ErrRPCNoTxInfo, + }, { + name: "handleGetMixpoolInfo: utxo set lookup failure", + handler: handleGetMixpoolInfo, + cmd: &types.GetMixpoolInfoCmd{}, + mockClock: &testClock{now: now}, + mockChain: func() *testRPCChain { + chain := defaultMockRPCChain() + chain.fetchUtxoEntryErr = errors.New("utxo entry error") + return chain + }(), + mockMixPooler: func() *testMixPooler { + mp := defaultMockMixPooler() + mp.epoch = epoch + mp.mixPRs = []*wire.MsgMixPairReq{pr1} + return mp + }(), + wantErr: true, + errCode: dcrjson.ErrRPCNoTxInfo, + }}) +} + func TestHandleGetMiningInfo(t *testing.T) { t.Parallel() diff --git a/internal/rpcserver/rpcserverhelp.go b/internal/rpcserver/rpcserverhelp.go index b3a1f9b8ad..0925408323 100644 --- a/internal/rpcserver/rpcserverhelp.go +++ b/internal/rpcserver/rpcserverhelp.go @@ -1,5 +1,5 @@ // Copyright (c) 2015 The btcsuite developers -// Copyright (c) 2015-2025 The Decred developers +// Copyright (c) 2015-2026 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -522,9 +522,38 @@ var helpDescsEnUS = map[string]string{ "getmixmessageresult-type": "Command type of the message", "getmixmessageresult-message": "Serialized message in hex encoding", - // GetMixPairRequests help. - "getmixpairrequests--synopsis": "Returns current set of mixing pair request messages from mixpool.", - "getmixpairrequests--result0": "JSON array of hex-encoded mixing pair request messages.", + // GetMixpoolInfo help. + "getmixpoolinfo--synopsis": "Returns the current state of the mixpool including timing of the next mix epoch and pending pair requests.", + "getmixpoolinfo--result0": "JSON object describing current mixpool state.", + + // GetMixpoolInfoResult help. + "getmixpoolinforesult-epoch": "Duration between mix epochs, in seconds", + "getmixpoolinforesult-nextepoch": "Unix timestamp of the next mix epoch", + "getmixpoolinforesult-pairings": "Pending pair requests grouped by mixing compatibility", + + // Pairing help. + "pairing-mixamount": "Amount of each mixed output, in DCR", + "pairing-scriptclass": "Script class of the mixed outputs", + "pairing-txversion": "Transaction version of the mix transaction", + "pairing-locktime": "Lock time of the mix transaction", + "pairing-pairingflags": "Pairing flags", + "pairing-pairrequests": "The pair requests matching these mixing parameters", + + // PairRequest help. + "pairrequest-hash": "Hash of the pair request message", + "pairrequest-identity": "Participant ephemeral public key identity as a hex string", + "pairrequest-messagecount": "Number of mixed outputs, each of value mixamount, the pair request is creating", + "pairrequest-inputvalue": "Total value of inputs contributed by the pair request, in DCR", + "pairrequest-utxos": "Unspent transaction outputs contributed by the pair request", + "pairrequest-expiry": "Block height at which the pair request expires", + + // PairRequestUTXO help. + "pairrequestutxo-txid": "The hash of the origin transaction", + "pairrequestutxo-vout": "The index of the output being contributed", + "pairrequestutxo-tree": "The tree of the output being contributed", + "pairrequestutxo-amountin": "The amount of the output, in DCR", + "pairrequestutxo-blockheight": "The height of the block containing the origin transaction", + "pairrequestutxo-scriptPubKey": "The public key script used to pay coins as a JSON object", // GetNetworkHashPSCmd help. "getnetworkhashps--synopsis": "Returns the estimated network hashes per second for the block heights provided by the parameters.", @@ -993,7 +1022,7 @@ var rpcResultTypes = map[types.Method][]any{ "getmempoolinfo": {(*types.GetMempoolInfoResult)(nil)}, "getmininginfo": {(*types.GetMiningInfoResult)(nil)}, "getmixmessage": {(*types.GetMixMessageResult)(nil)}, - "getmixpairrequests": {(*[]string)(nil)}, + "getmixpoolinfo": {(*types.GetMixpoolInfoResult)(nil)}, "getnettotals": {(*types.GetNetTotalsResult)(nil)}, "getnetworkhashps": {(*int64)(nil)}, "getnetworkinfo": {(*[]types.GetNetworkInfoResult)(nil)}, diff --git a/rpc/jsonrpc/types/chainsvrcmds.go b/rpc/jsonrpc/types/chainsvrcmds.go index 276f939264..e4bfc25ee3 100644 --- a/rpc/jsonrpc/types/chainsvrcmds.go +++ b/rpc/jsonrpc/types/chainsvrcmds.go @@ -565,13 +565,13 @@ func NewGetMixMessageCmd(hash string) *GetMixMessageCmd { } } -// GetMixPairRequestsCmd defines the getmixpairrequests JSON-RPC command. -type GetMixPairRequestsCmd struct{} +// GetMixpoolInfoCmd defines the getmixpoolinfo JSON-RPC command. +type GetMixpoolInfoCmd struct{} -// NewGetMixPairRequestsCmd returns a new instance which can be used to issue a -// getmixpairrequests JSON-RPC command. -func NewGetMixPairRequestsCmd() *GetMixPairRequestsCmd { - return &GetMixPairRequestsCmd{} +// NewGetMixpoolInfoCmd returns a new instance which can be used to issue a +// getmixpoolinfo JSON-RPC command. +func NewGetMixpoolInfoCmd() *GetMixpoolInfoCmd { + return &GetMixpoolInfoCmd{} } // GetNetworkInfoCmd defines the getnetworkinfo JSON-RPC command. @@ -1184,7 +1184,7 @@ func init() { dcrjson.MustRegister(Method("getmempoolinfo"), (*GetMempoolInfoCmd)(nil), flags) dcrjson.MustRegister(Method("getmininginfo"), (*GetMiningInfoCmd)(nil), flags) dcrjson.MustRegister(Method("getmixmessage"), (*GetMixMessageCmd)(nil), flags) - dcrjson.MustRegister(Method("getmixpairrequests"), (*GetMixPairRequestsCmd)(nil), flags) + dcrjson.MustRegister(Method("getmixpoolinfo"), (*GetMixpoolInfoCmd)(nil), flags) dcrjson.MustRegister(Method("getnetworkinfo"), (*GetNetworkInfoCmd)(nil), flags) dcrjson.MustRegister(Method("getnettotals"), (*GetNetTotalsCmd)(nil), flags) dcrjson.MustRegister(Method("getnetworkhashps"), (*GetNetworkHashPSCmd)(nil), flags) diff --git a/rpc/jsonrpc/types/chainsvrcmds_test.go b/rpc/jsonrpc/types/chainsvrcmds_test.go index a33f15869c..36af5a3dc7 100644 --- a/rpc/jsonrpc/types/chainsvrcmds_test.go +++ b/rpc/jsonrpc/types/chainsvrcmds_test.go @@ -465,15 +465,15 @@ func TestChainSvrCmds(t *testing.T) { unmarshalled: &GetMixMessageCmd{Hash: "123"}, }, { - name: "getmixpairrequests", + name: "getmixpoolinfo", newCmd: func() (interface{}, error) { - return dcrjson.NewCmd(Method("getmixpairrequests")) + return dcrjson.NewCmd(Method("getmixpoolinfo")) }, staticCmd: func() interface{} { - return NewGetMixPairRequestsCmd() + return NewGetMixpoolInfoCmd() }, - marshalled: `{"jsonrpc":"1.0","method":"getmixpairrequests","params":[],"id":1}`, - unmarshalled: &GetMixPairRequestsCmd{}, + marshalled: `{"jsonrpc":"1.0","method":"getmixpoolinfo","params":[],"id":1}`, + unmarshalled: &GetMixpoolInfoCmd{}, }, { name: "getnetworkinfo", diff --git a/rpc/jsonrpc/types/chainsvrresults.go b/rpc/jsonrpc/types/chainsvrresults.go index d18f9a6804..10a2d39355 100644 --- a/rpc/jsonrpc/types/chainsvrresults.go +++ b/rpc/jsonrpc/types/chainsvrresults.go @@ -1,5 +1,5 @@ // Copyright (c) 2014 The btcsuite developers -// Copyright (c) 2015-2024 The Decred developers +// Copyright (c) 2015-2026 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -240,6 +240,46 @@ type GetMixMessageResult struct { Message string `json:"message"` } +// GetMixpoolInfoResult models the data from the getmixpoolinfo command. +type GetMixpoolInfoResult struct { + Epoch int64 `json:"epoch"` + NextEpoch int64 `json:"nextepoch"` + Pairings []Pairing `json:"pairings"` +} + +// Pairing models a group of pair requests which share the same mix parameters +// (i.e. MixAmount, ScriptClass, TxVersion, LockTime, and PairingFlags) and are +// thus compatible to mix together. +type Pairing struct { + MixAmount float64 `json:"mixamount"` + ScriptClass string `json:"scriptclass"` + TxVersion uint16 `json:"txversion"` + LockTime uint32 `json:"locktime"` + PairingFlags uint8 `json:"pairingflags"` + PairRequests []PairRequest `json:"pairrequests"` +} + +// PairRequest models a single pair request. +type PairRequest struct { + Hash string `json:"hash"` + Identity string `json:"identity"` + MessageCount uint32 `json:"messagecount"` + InputValue float64 `json:"inputvalue"` + UTXOs []PairRequestUTXO `json:"utxos"` + Expiry uint32 `json:"expiry"` +} + +// PairRequestUTXO models an unspent transaction output contributed to a mixing +// pair request. +type PairRequestUTXO struct { + Txid string `json:"txid"` + Vout uint32 `json:"vout"` + Tree int8 `json:"tree"` + AmountIn float64 `json:"amountin"` + BlockHeight uint32 `json:"blockheight"` + ScriptPubKey ScriptPubKeyResult `json:"scriptPubKey"` +} + // LocalAddressesResult models the localaddresses data from the getnetworkinfo // command. type LocalAddressesResult struct {