From f04859eae01781926de10a9467522f9bdb435cda Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Thu, 13 Feb 2025 16:37:48 +0100 Subject: [PATCH 1/8] chore: add TxHash field, WIP --- internal/rpc/core/mempool.go | 4 ++-- light/proxy/routes.go | 2 +- light/rpc/client.go | 4 ++-- rpc/client/http/http.go | 8 +++++--- rpc/client/interface.go | 2 +- rpc/client/local/local.go | 3 ++- rpc/coretypes/requests.go | 5 +++-- 7 files changed, 16 insertions(+), 12 deletions(-) diff --git a/internal/rpc/core/mempool.go b/internal/rpc/core/mempool.go index 464e15b3ef..36b5ae52ab 100644 --- a/internal/rpc/core/mempool.go +++ b/internal/rpc/core/mempool.go @@ -171,7 +171,7 @@ func (env *Environment) BroadcastTxCommit(ctx context.Context, req *coretypes.Re } } -// UnconfirmedTxs gets unconfirmed transactions from the mempool in order of priority +// UnconfirmedTxs gets unconfirmed transactions from the mempool in order of priority. // More: https://docs.tendermint.com/master/rpc/#/Info/unconfirmed_txs func (env *Environment) UnconfirmedTxs(_ctx context.Context, req *coretypes.RequestUnconfirmedTxs) (*coretypes.ResultUnconfirmedTxs, error) { totalCount := env.Mempool.Size() @@ -182,7 +182,7 @@ func (env *Environment) UnconfirmedTxs(_ctx context.Context, req *coretypes.Requ } skipCount := validateSkipCount(page, perPage) - + // TODO: filter by tx hash here txs := env.Mempool.ReapMaxTxs(skipCount + tmmath.MinInt(perPage, totalCount-skipCount)) result := txs[skipCount:] diff --git a/light/proxy/routes.go b/light/proxy/routes.go index 3bc23f1251..b094f0e27f 100644 --- a/light/proxy/routes.go +++ b/light/proxy/routes.go @@ -137,7 +137,7 @@ func (p proxyService) TxSearch(ctx context.Context, req *coretypes.RequestTxSear } func (p proxyService) UnconfirmedTxs(ctx context.Context, req *coretypes.RequestUnconfirmedTxs) (*coretypes.ResultUnconfirmedTxs, error) { - return p.Client.UnconfirmedTxs(ctx, req.Page.IntPtr(), req.PerPage.IntPtr()) + return p.Client.UnconfirmedTxs(ctx, req.Page.IntPtr(), req.PerPage.IntPtr(), req.TxHash) } func (p proxyService) Unsubscribe(ctx context.Context, req *coretypes.RequestUnsubscribe) (*coretypes.ResultUnsubscribe, error) { diff --git a/light/rpc/client.go b/light/rpc/client.go index af282d6fa6..450a3f86f5 100644 --- a/light/rpc/client.go +++ b/light/rpc/client.go @@ -236,8 +236,8 @@ func (c *Client) BroadcastTx(ctx context.Context, tx types.Tx) (*coretypes.Resul return c.next.BroadcastTx(ctx, tx) } -func (c *Client) UnconfirmedTxs(ctx context.Context, page, perPage *int) (*coretypes.ResultUnconfirmedTxs, error) { - return c.next.UnconfirmedTxs(ctx, page, perPage) +func (c *Client) UnconfirmedTxs(ctx context.Context, page, perPage *int, txHash []byte) (*coretypes.ResultUnconfirmedTxs, error) { + return c.next.UnconfirmedTxs(ctx, page, perPage, txHash) } func (c *Client) NumUnconfirmedTxs(ctx context.Context) (*coretypes.ResultUnconfirmedTxs, error) { diff --git a/rpc/client/http/http.go b/rpc/client/http/http.go index 7708e96196..efda859e02 100644 --- a/rpc/client/http/http.go +++ b/rpc/client/http/http.go @@ -8,6 +8,7 @@ import ( "time" "github.com/dashpay/tenderdash/libs/bytes" + tmbytes "github.com/dashpay/tenderdash/libs/bytes" rpcclient "github.com/dashpay/tenderdash/rpc/client" "github.com/dashpay/tenderdash/rpc/coretypes" jsonrpcclient "github.com/dashpay/tenderdash/rpc/jsonrpc/client" @@ -208,11 +209,11 @@ func (c *baseRPCClient) ABCIInfo(ctx context.Context) (*coretypes.ResultABCIInfo return result, nil } -func (c *baseRPCClient) ABCIQuery(ctx context.Context, path string, data bytes.HexBytes) (*coretypes.ResultABCIQuery, error) { +func (c *baseRPCClient) ABCIQuery(ctx context.Context, path string, data tmbytes.HexBytes) (*coretypes.ResultABCIQuery, error) { return c.ABCIQueryWithOptions(ctx, path, data, rpcclient.DefaultABCIQueryOptions) } -func (c *baseRPCClient) ABCIQueryWithOptions(ctx context.Context, path string, data bytes.HexBytes, opts rpcclient.ABCIQueryOptions) (*coretypes.ResultABCIQuery, error) { +func (c *baseRPCClient) ABCIQueryWithOptions(ctx context.Context, path string, data tmbytes.HexBytes, opts rpcclient.ABCIQueryOptions) (*coretypes.ResultABCIQuery, error) { result := new(coretypes.ResultABCIQuery) if err := c.caller.Call(ctx, "abci_query", &coretypes.RequestABCIQuery{ Path: path, @@ -255,12 +256,13 @@ func (c *baseRPCClient) broadcastTX(ctx context.Context, route string, tx types. return result, nil } -func (c *baseRPCClient) UnconfirmedTxs(ctx context.Context, page *int, perPage *int) (*coretypes.ResultUnconfirmedTxs, error) { +func (c *baseRPCClient) UnconfirmedTxs(ctx context.Context, page *int, perPage *int, txHash []byte) (*coretypes.ResultUnconfirmedTxs, error) { result := new(coretypes.ResultUnconfirmedTxs) if err := c.caller.Call(ctx, "unconfirmed_txs", &coretypes.RequestUnconfirmedTxs{ Page: coretypes.Int64Ptr(page), PerPage: coretypes.Int64Ptr(perPage), + TxHash: tmbytes.HexBytes(txHash), }, result); err != nil { return nil, err } diff --git a/rpc/client/interface.go b/rpc/client/interface.go index 7f011a828a..c87be8aac6 100644 --- a/rpc/client/interface.go +++ b/rpc/client/interface.go @@ -163,7 +163,7 @@ type SubscriptionClient interface { // MempoolClient shows us data about current mempool state. type MempoolClient interface { - UnconfirmedTxs(ctx context.Context, page, perPage *int) (*coretypes.ResultUnconfirmedTxs, error) + UnconfirmedTxs(ctx context.Context, page, perPage *int, txHash []byte) (*coretypes.ResultUnconfirmedTxs, error) NumUnconfirmedTxs(context.Context) (*coretypes.ResultUnconfirmedTxs, error) CheckTx(context.Context, types.Tx) (*coretypes.ResultCheckTx, error) RemoveTx(context.Context, types.TxKey) error diff --git a/rpc/client/local/local.go b/rpc/client/local/local.go index 6408a377ba..c2c58dd7d5 100644 --- a/rpc/client/local/local.go +++ b/rpc/client/local/local.go @@ -99,10 +99,11 @@ func (c *Local) BroadcastTxSync(ctx context.Context, tx types.Tx) (*coretypes.Re return c.env.BroadcastTxSync(ctx, &coretypes.RequestBroadcastTx{Tx: tx}) } -func (c *Local) UnconfirmedTxs(ctx context.Context, page, perPage *int) (*coretypes.ResultUnconfirmedTxs, error) { +func (c *Local) UnconfirmedTxs(ctx context.Context, page, perPage *int, txHash []byte) (*coretypes.ResultUnconfirmedTxs, error) { return c.env.UnconfirmedTxs(ctx, &coretypes.RequestUnconfirmedTxs{ Page: coretypes.Int64Ptr(page), PerPage: coretypes.Int64Ptr(perPage), + TxHash: txHash, }) } diff --git a/rpc/coretypes/requests.go b/rpc/coretypes/requests.go index 18870f50f3..b826906469 100644 --- a/rpc/coretypes/requests.go +++ b/rpc/coretypes/requests.go @@ -76,8 +76,9 @@ type RequestConsensusParams struct { } type RequestUnconfirmedTxs struct { - Page *Int64 `json:"page"` - PerPage *Int64 `json:"per_page"` + Page *Int64 `json:"page"` + PerPage *Int64 `json:"per_page"` + TxHash bytes.HexBytes `json:"tx_hash,omitempty"` } type RequestBroadcastTx struct { From 0a0583a9fed3ee0323e509ef3c7ae76a964ab4a3 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Fri, 21 Feb 2025 10:55:58 +0100 Subject: [PATCH 2/8] chore: update mockery --- rpc/client/mocks/client.go | 29 +++++++++++++++-------------- rpc/client/mocks/remoteclient.go | 29 +++++++++++++++-------------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/rpc/client/mocks/client.go b/rpc/client/mocks/client.go index 0b4d6fbf46..b8577328f1 100644 --- a/rpc/client/mocks/client.go +++ b/rpc/client/mocks/client.go @@ -1911,9 +1911,9 @@ func (_c *Client_TxSearch_Call) RunAndReturn(run func(context.Context, string, b return _c } -// UnconfirmedTxs provides a mock function with given fields: ctx, page, perPage -func (_m *Client) UnconfirmedTxs(ctx context.Context, page *int, perPage *int) (*coretypes.ResultUnconfirmedTxs, error) { - ret := _m.Called(ctx, page, perPage) +// UnconfirmedTxs provides a mock function with given fields: ctx, page, perPage, txHash +func (_m *Client) UnconfirmedTxs(ctx context.Context, page *int, perPage *int, txHash []byte) (*coretypes.ResultUnconfirmedTxs, error) { + ret := _m.Called(ctx, page, perPage, txHash) if len(ret) == 0 { panic("no return value specified for UnconfirmedTxs") @@ -1921,19 +1921,19 @@ func (_m *Client) UnconfirmedTxs(ctx context.Context, page *int, perPage *int) ( var r0 *coretypes.ResultUnconfirmedTxs var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *int, *int) (*coretypes.ResultUnconfirmedTxs, error)); ok { - return rf(ctx, page, perPage) + if rf, ok := ret.Get(0).(func(context.Context, *int, *int, []byte) (*coretypes.ResultUnconfirmedTxs, error)); ok { + return rf(ctx, page, perPage, txHash) } - if rf, ok := ret.Get(0).(func(context.Context, *int, *int) *coretypes.ResultUnconfirmedTxs); ok { - r0 = rf(ctx, page, perPage) + if rf, ok := ret.Get(0).(func(context.Context, *int, *int, []byte) *coretypes.ResultUnconfirmedTxs); ok { + r0 = rf(ctx, page, perPage, txHash) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*coretypes.ResultUnconfirmedTxs) } } - if rf, ok := ret.Get(1).(func(context.Context, *int, *int) error); ok { - r1 = rf(ctx, page, perPage) + if rf, ok := ret.Get(1).(func(context.Context, *int, *int, []byte) error); ok { + r1 = rf(ctx, page, perPage, txHash) } else { r1 = ret.Error(1) } @@ -1950,13 +1950,14 @@ type Client_UnconfirmedTxs_Call struct { // - ctx context.Context // - page *int // - perPage *int -func (_e *Client_Expecter) UnconfirmedTxs(ctx interface{}, page interface{}, perPage interface{}) *Client_UnconfirmedTxs_Call { - return &Client_UnconfirmedTxs_Call{Call: _e.mock.On("UnconfirmedTxs", ctx, page, perPage)} +// - txHash []byte +func (_e *Client_Expecter) UnconfirmedTxs(ctx interface{}, page interface{}, perPage interface{}, txHash interface{}) *Client_UnconfirmedTxs_Call { + return &Client_UnconfirmedTxs_Call{Call: _e.mock.On("UnconfirmedTxs", ctx, page, perPage, txHash)} } -func (_c *Client_UnconfirmedTxs_Call) Run(run func(ctx context.Context, page *int, perPage *int)) *Client_UnconfirmedTxs_Call { +func (_c *Client_UnconfirmedTxs_Call) Run(run func(ctx context.Context, page *int, perPage *int, txHash []byte)) *Client_UnconfirmedTxs_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*int), args[2].(*int)) + run(args[0].(context.Context), args[1].(*int), args[2].(*int), args[3].([]byte)) }) return _c } @@ -1966,7 +1967,7 @@ func (_c *Client_UnconfirmedTxs_Call) Return(_a0 *coretypes.ResultUnconfirmedTxs return _c } -func (_c *Client_UnconfirmedTxs_Call) RunAndReturn(run func(context.Context, *int, *int) (*coretypes.ResultUnconfirmedTxs, error)) *Client_UnconfirmedTxs_Call { +func (_c *Client_UnconfirmedTxs_Call) RunAndReturn(run func(context.Context, *int, *int, []byte) (*coretypes.ResultUnconfirmedTxs, error)) *Client_UnconfirmedTxs_Call { _c.Call.Return(run) return _c } diff --git a/rpc/client/mocks/remoteclient.go b/rpc/client/mocks/remoteclient.go index 91f48561fc..74b8f0de5c 100644 --- a/rpc/client/mocks/remoteclient.go +++ b/rpc/client/mocks/remoteclient.go @@ -1956,9 +1956,9 @@ func (_c *RemoteClient_TxSearch_Call) RunAndReturn(run func(context.Context, str return _c } -// UnconfirmedTxs provides a mock function with given fields: ctx, page, perPage -func (_m *RemoteClient) UnconfirmedTxs(ctx context.Context, page *int, perPage *int) (*coretypes.ResultUnconfirmedTxs, error) { - ret := _m.Called(ctx, page, perPage) +// UnconfirmedTxs provides a mock function with given fields: ctx, page, perPage, txHash +func (_m *RemoteClient) UnconfirmedTxs(ctx context.Context, page *int, perPage *int, txHash []byte) (*coretypes.ResultUnconfirmedTxs, error) { + ret := _m.Called(ctx, page, perPage, txHash) if len(ret) == 0 { panic("no return value specified for UnconfirmedTxs") @@ -1966,19 +1966,19 @@ func (_m *RemoteClient) UnconfirmedTxs(ctx context.Context, page *int, perPage * var r0 *coretypes.ResultUnconfirmedTxs var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *int, *int) (*coretypes.ResultUnconfirmedTxs, error)); ok { - return rf(ctx, page, perPage) + if rf, ok := ret.Get(0).(func(context.Context, *int, *int, []byte) (*coretypes.ResultUnconfirmedTxs, error)); ok { + return rf(ctx, page, perPage, txHash) } - if rf, ok := ret.Get(0).(func(context.Context, *int, *int) *coretypes.ResultUnconfirmedTxs); ok { - r0 = rf(ctx, page, perPage) + if rf, ok := ret.Get(0).(func(context.Context, *int, *int, []byte) *coretypes.ResultUnconfirmedTxs); ok { + r0 = rf(ctx, page, perPage, txHash) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*coretypes.ResultUnconfirmedTxs) } } - if rf, ok := ret.Get(1).(func(context.Context, *int, *int) error); ok { - r1 = rf(ctx, page, perPage) + if rf, ok := ret.Get(1).(func(context.Context, *int, *int, []byte) error); ok { + r1 = rf(ctx, page, perPage, txHash) } else { r1 = ret.Error(1) } @@ -1995,13 +1995,14 @@ type RemoteClient_UnconfirmedTxs_Call struct { // - ctx context.Context // - page *int // - perPage *int -func (_e *RemoteClient_Expecter) UnconfirmedTxs(ctx interface{}, page interface{}, perPage interface{}) *RemoteClient_UnconfirmedTxs_Call { - return &RemoteClient_UnconfirmedTxs_Call{Call: _e.mock.On("UnconfirmedTxs", ctx, page, perPage)} +// - txHash []byte +func (_e *RemoteClient_Expecter) UnconfirmedTxs(ctx interface{}, page interface{}, perPage interface{}, txHash interface{}) *RemoteClient_UnconfirmedTxs_Call { + return &RemoteClient_UnconfirmedTxs_Call{Call: _e.mock.On("UnconfirmedTxs", ctx, page, perPage, txHash)} } -func (_c *RemoteClient_UnconfirmedTxs_Call) Run(run func(ctx context.Context, page *int, perPage *int)) *RemoteClient_UnconfirmedTxs_Call { +func (_c *RemoteClient_UnconfirmedTxs_Call) Run(run func(ctx context.Context, page *int, perPage *int, txHash []byte)) *RemoteClient_UnconfirmedTxs_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*int), args[2].(*int)) + run(args[0].(context.Context), args[1].(*int), args[2].(*int), args[3].([]byte)) }) return _c } @@ -2011,7 +2012,7 @@ func (_c *RemoteClient_UnconfirmedTxs_Call) Return(_a0 *coretypes.ResultUnconfir return _c } -func (_c *RemoteClient_UnconfirmedTxs_Call) RunAndReturn(run func(context.Context, *int, *int) (*coretypes.ResultUnconfirmedTxs, error)) *RemoteClient_UnconfirmedTxs_Call { +func (_c *RemoteClient_UnconfirmedTxs_Call) RunAndReturn(run func(context.Context, *int, *int, []byte) (*coretypes.ResultUnconfirmedTxs, error)) *RemoteClient_UnconfirmedTxs_Call { _c.Call.Return(run) return _c } From 1ec9dde3de29eda6bdd5982ae4459d56638a16e8 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Fri, 21 Feb 2025 13:09:53 +0100 Subject: [PATCH 3/8] feat(rpc): add new request type --- internal/consensus/replay_stubs.go | 1 + internal/mempool/mempool.go | 11 ++++ internal/mempool/mocks/mempool.go | 48 ++++++++++++++++ internal/mempool/types.go | 3 + internal/rpc/core/mempool.go | 15 +++++ internal/rpc/core/routes.go | 1 + light/proxy/routes.go | 6 +- light/rpc/client.go | 8 ++- rpc/client/http/http.go | 21 +++++-- rpc/client/interface.go | 3 +- rpc/client/local/local.go | 6 +- rpc/client/mocks/client.go | 88 +++++++++++++++++++++++++----- rpc/client/mocks/remoteclient.go | 88 +++++++++++++++++++++++++----- rpc/coretypes/requests.go | 10 +++- rpc/coretypes/responses.go | 5 ++ types/tx.go | 7 ++- 16 files changed, 274 insertions(+), 47 deletions(-) diff --git a/internal/consensus/replay_stubs.go b/internal/consensus/replay_stubs.go index 9fce5090a2..99726c3fc0 100644 --- a/internal/consensus/replay_stubs.go +++ b/internal/consensus/replay_stubs.go @@ -39,6 +39,7 @@ func (emptyMempool) Update( ) error { return nil } +func (emptyMempool) GetTxByHash(_ types.TxKey) types.Tx { return nil } func (emptyMempool) Flush() {} func (emptyMempool) FlushAppConn(_ctx context.Context) error { return nil } func (emptyMempool) TxsAvailable() <-chan struct{} { return make(chan struct{}) } diff --git a/internal/mempool/mempool.go b/internal/mempool/mempool.go index c268dff0fd..fd2de9f79d 100644 --- a/internal/mempool/mempool.go +++ b/internal/mempool/mempool.go @@ -260,6 +260,17 @@ func (txmp *TxMempool) CheckTx( return txmp.addNewTransaction(wtx, rsp) } +// GetTxByHash returns transaction by key from the mempool. If the transaction +// does not exist, it returns nil. +func (txmp *TxMempool) GetTxByHash(txHash types.TxKey) types.Tx { + if elt, ok := txmp.txByKey[txHash]; ok { + w := elt.Value.(*WrappedTx) + return w.tx + } + + return nil +} + // RemoveTxByKey removes the transaction with the specified key from the // mempool. It reports an error if no such transaction exists. This operation // does not remove the transaction from the cache. diff --git a/internal/mempool/mocks/mempool.go b/internal/mempool/mocks/mempool.go index b8a04669f3..db1c0fdc98 100644 --- a/internal/mempool/mocks/mempool.go +++ b/internal/mempool/mocks/mempool.go @@ -186,6 +186,54 @@ func (_c *Mempool_FlushAppConn_Call) RunAndReturn(run func(context.Context) erro return _c } +// GetTxByHash provides a mock function with given fields: txHash +func (_m *Mempool) GetTxByHash(txHash types.TxKey) types.Tx { + ret := _m.Called(txHash) + + if len(ret) == 0 { + panic("no return value specified for GetTxByHash") + } + + var r0 types.Tx + if rf, ok := ret.Get(0).(func(types.TxKey) types.Tx); ok { + r0 = rf(txHash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(types.Tx) + } + } + + return r0 +} + +// Mempool_GetTxByHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTxByHash' +type Mempool_GetTxByHash_Call struct { + *mock.Call +} + +// GetTxByHash is a helper method to define mock.On call +// - txHash types.TxKey +func (_e *Mempool_Expecter) GetTxByHash(txHash interface{}) *Mempool_GetTxByHash_Call { + return &Mempool_GetTxByHash_Call{Call: _e.mock.On("GetTxByHash", txHash)} +} + +func (_c *Mempool_GetTxByHash_Call) Run(run func(txHash types.TxKey)) *Mempool_GetTxByHash_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(types.TxKey)) + }) + return _c +} + +func (_c *Mempool_GetTxByHash_Call) Return(_a0 types.Tx) *Mempool_GetTxByHash_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Mempool_GetTxByHash_Call) RunAndReturn(run func(types.TxKey) types.Tx) *Mempool_GetTxByHash_Call { + _c.Call.Return(run) + return _c +} + // Lock provides a mock function with no fields func (_m *Mempool) Lock() { _m.Called() diff --git a/internal/mempool/types.go b/internal/mempool/types.go index 48dc52041d..a51d5e400a 100644 --- a/internal/mempool/types.go +++ b/internal/mempool/types.go @@ -40,6 +40,9 @@ type Mempool interface { // from the mempool. RemoveTxByKey(txKey types.TxKey) error + // GetTxByHash returns a transaction from the mempool, if it exists, or nil. + GetTxByHash(txHash types.TxKey) types.Tx + // ReapMaxBytesMaxGas reaps transactions from the mempool up to maxBytes // bytes total with the condition that the total gasWanted must be less than // maxGas. diff --git a/internal/rpc/core/mempool.go b/internal/rpc/core/mempool.go index 36b5ae52ab..1d7a9728cb 100644 --- a/internal/rpc/core/mempool.go +++ b/internal/rpc/core/mempool.go @@ -12,6 +12,7 @@ import ( "github.com/dashpay/tenderdash/internal/state/indexer" tmmath "github.com/dashpay/tenderdash/libs/math" "github.com/dashpay/tenderdash/rpc/coretypes" + "github.com/dashpay/tenderdash/types" ) //----------------------------------------------------------------------------- @@ -194,6 +195,20 @@ func (env *Environment) UnconfirmedTxs(_ctx context.Context, req *coretypes.Requ }, nil } +// return single unconfirmed transaction, matching req.TxHash +func (env *Environment) UnconfirmedTx(_ctx context.Context, req *coretypes.RequestUnconfirmedTx) (*coretypes.ResultUnconfirmedTx, error) { + if req == nil || req.TxHash.IsZero() { + return nil, errors.New("you mustprovide transaction hash in tx_hash") + } + + tx := env.Mempool.GetTxByHash(types.TxKey(req.TxHash)) + if tx == nil { + return nil, fmt.Errorf("transaction %X not found", req.TxHash) + } + + return &coretypes.ResultUnconfirmedTx{Tx: tx}, nil +} + // NumUnconfirmedTxs gets number of unconfirmed transactions. // More: https://docs.tendermint.com/master/rpc/#/Info/num_unconfirmed_txs func (env *Environment) NumUnconfirmedTxs(_ctx context.Context) (*coretypes.ResultUnconfirmedTxs, error) { diff --git a/internal/rpc/core/routes.go b/internal/rpc/core/routes.go index 49c493e1fa..75b602ba9d 100644 --- a/internal/rpc/core/routes.go +++ b/internal/rpc/core/routes.go @@ -113,6 +113,7 @@ type RPCService interface { Tx(ctx context.Context, req *coretypes.RequestTx) (*coretypes.ResultTx, error) TxSearch(ctx context.Context, req *coretypes.RequestTxSearch) (*coretypes.ResultTxSearch, error) UnconfirmedTxs(ctx context.Context, req *coretypes.RequestUnconfirmedTxs) (*coretypes.ResultUnconfirmedTxs, error) + UnconfirmedTx(ctx context.Context, req *coretypes.RequestUnconfirmedTx) (*coretypes.ResultUnconfirmedTx, error) Unsubscribe(ctx context.Context, req *coretypes.RequestUnsubscribe) (*coretypes.ResultUnsubscribe, error) UnsubscribeAll(ctx context.Context) (*coretypes.ResultUnsubscribe, error) Validators(ctx context.Context, req *coretypes.RequestValidators) (*coretypes.ResultValidators, error) diff --git a/light/proxy/routes.go b/light/proxy/routes.go index b094f0e27f..5e83134058 100644 --- a/light/proxy/routes.go +++ b/light/proxy/routes.go @@ -137,7 +137,11 @@ func (p proxyService) TxSearch(ctx context.Context, req *coretypes.RequestTxSear } func (p proxyService) UnconfirmedTxs(ctx context.Context, req *coretypes.RequestUnconfirmedTxs) (*coretypes.ResultUnconfirmedTxs, error) { - return p.Client.UnconfirmedTxs(ctx, req.Page.IntPtr(), req.PerPage.IntPtr(), req.TxHash) + return p.Client.UnconfirmedTxs(ctx, req.Page.IntPtr(), req.PerPage.IntPtr()) +} + +func (p proxyService) UnconfirmedTx(ctx context.Context, req *coretypes.RequestUnconfirmedTx) (*coretypes.ResultUnconfirmedTx, error) { + return p.Client.UnconfirmedTx(ctx, req.TxHash) } func (p proxyService) Unsubscribe(ctx context.Context, req *coretypes.RequestUnsubscribe) (*coretypes.ResultUnsubscribe, error) { diff --git a/light/rpc/client.go b/light/rpc/client.go index 450a3f86f5..9a4702ec20 100644 --- a/light/rpc/client.go +++ b/light/rpc/client.go @@ -236,8 +236,12 @@ func (c *Client) BroadcastTx(ctx context.Context, tx types.Tx) (*coretypes.Resul return c.next.BroadcastTx(ctx, tx) } -func (c *Client) UnconfirmedTxs(ctx context.Context, page, perPage *int, txHash []byte) (*coretypes.ResultUnconfirmedTxs, error) { - return c.next.UnconfirmedTxs(ctx, page, perPage, txHash) +func (c *Client) UnconfirmedTxs(ctx context.Context, page, perPage *int) (*coretypes.ResultUnconfirmedTxs, error) { + return c.next.UnconfirmedTxs(ctx, page, perPage) +} + +func (c *Client) UnconfirmedTx(ctx context.Context, txHash []byte) (*coretypes.ResultUnconfirmedTx, error) { + return c.next.UnconfirmedTx(ctx, txHash) } func (c *Client) NumUnconfirmedTxs(ctx context.Context) (*coretypes.ResultUnconfirmedTxs, error) { diff --git a/rpc/client/http/http.go b/rpc/client/http/http.go index efda859e02..069c79555c 100644 --- a/rpc/client/http/http.go +++ b/rpc/client/http/http.go @@ -7,7 +7,6 @@ import ( "net/http" "time" - "github.com/dashpay/tenderdash/libs/bytes" tmbytes "github.com/dashpay/tenderdash/libs/bytes" rpcclient "github.com/dashpay/tenderdash/rpc/client" "github.com/dashpay/tenderdash/rpc/coretypes" @@ -256,13 +255,23 @@ func (c *baseRPCClient) broadcastTX(ctx context.Context, route string, tx types. return result, nil } -func (c *baseRPCClient) UnconfirmedTxs(ctx context.Context, page *int, perPage *int, txHash []byte) (*coretypes.ResultUnconfirmedTxs, error) { +func (c *baseRPCClient) UnconfirmedTxs(ctx context.Context, page *int, perPage *int) (*coretypes.ResultUnconfirmedTxs, error) { result := new(coretypes.ResultUnconfirmedTxs) if err := c.caller.Call(ctx, "unconfirmed_txs", &coretypes.RequestUnconfirmedTxs{ Page: coretypes.Int64Ptr(page), PerPage: coretypes.Int64Ptr(perPage), - TxHash: tmbytes.HexBytes(txHash), + }, result); err != nil { + return nil, err + } + return result, nil +} + +func (c *baseRPCClient) UnconfirmedTx(ctx context.Context, txHash []byte) (*coretypes.ResultUnconfirmedTx, error) { + result := new(coretypes.ResultUnconfirmedTx) + + if err := c.caller.Call(ctx, "unconfirmed_tx", &coretypes.RequestUnconfirmedTx{ + TxHash: tmbytes.HexBytes(txHash), }, result); err != nil { return nil, err } @@ -381,7 +390,7 @@ func (c *baseRPCClient) Block(ctx context.Context, height *int64) (*coretypes.Re return result, nil } -func (c *baseRPCClient) BlockByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultBlock, error) { +func (c *baseRPCClient) BlockByHash(ctx context.Context, hash tmbytes.HexBytes) (*coretypes.ResultBlock, error) { result := new(coretypes.ResultBlock) if err := c.caller.Call(ctx, "block_by_hash", &coretypes.RequestBlockByHash{Hash: hash}, result); err != nil { return nil, err @@ -409,7 +418,7 @@ func (c *baseRPCClient) Header(ctx context.Context, height *int64) (*coretypes.R return result, nil } -func (c *baseRPCClient) HeaderByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultHeader, error) { +func (c *baseRPCClient) HeaderByHash(ctx context.Context, hash tmbytes.HexBytes) (*coretypes.ResultHeader, error) { result := new(coretypes.ResultHeader) if err := c.caller.Call(ctx, "header_by_hash", &coretypes.RequestBlockByHash{ Hash: hash, @@ -435,7 +444,7 @@ func (c *baseRPCClient) Commit(ctx context.Context, height *int64) (*coretypes.R return result, nil } -func (c *baseRPCClient) Tx(ctx context.Context, hash bytes.HexBytes, prove bool) (*coretypes.ResultTx, error) { +func (c *baseRPCClient) Tx(ctx context.Context, hash tmbytes.HexBytes, prove bool) (*coretypes.ResultTx, error) { result := new(coretypes.ResultTx) if err := c.caller.Call(ctx, "tx", &coretypes.RequestTx{Hash: hash, Prove: prove}, result); err != nil { return nil, err diff --git a/rpc/client/interface.go b/rpc/client/interface.go index c87be8aac6..6cc4d3a04a 100644 --- a/rpc/client/interface.go +++ b/rpc/client/interface.go @@ -163,7 +163,8 @@ type SubscriptionClient interface { // MempoolClient shows us data about current mempool state. type MempoolClient interface { - UnconfirmedTxs(ctx context.Context, page, perPage *int, txHash []byte) (*coretypes.ResultUnconfirmedTxs, error) + UnconfirmedTxs(ctx context.Context, page, perPage *int) (*coretypes.ResultUnconfirmedTxs, error) + UnconfirmedTx(ctx context.Context, txHash []byte) (*coretypes.ResultUnconfirmedTx, error) NumUnconfirmedTxs(context.Context) (*coretypes.ResultUnconfirmedTxs, error) CheckTx(context.Context, types.Tx) (*coretypes.ResultCheckTx, error) RemoveTx(context.Context, types.TxKey) error diff --git a/rpc/client/local/local.go b/rpc/client/local/local.go index c2c58dd7d5..7b09d25df6 100644 --- a/rpc/client/local/local.go +++ b/rpc/client/local/local.go @@ -99,13 +99,15 @@ func (c *Local) BroadcastTxSync(ctx context.Context, tx types.Tx) (*coretypes.Re return c.env.BroadcastTxSync(ctx, &coretypes.RequestBroadcastTx{Tx: tx}) } -func (c *Local) UnconfirmedTxs(ctx context.Context, page, perPage *int, txHash []byte) (*coretypes.ResultUnconfirmedTxs, error) { +func (c *Local) UnconfirmedTxs(ctx context.Context, page, perPage *int) (*coretypes.ResultUnconfirmedTxs, error) { return c.env.UnconfirmedTxs(ctx, &coretypes.RequestUnconfirmedTxs{ Page: coretypes.Int64Ptr(page), PerPage: coretypes.Int64Ptr(perPage), - TxHash: txHash, }) } +func (c *Local) UnconfirmedTx(ctx context.Context, txHash []byte) (*coretypes.ResultUnconfirmedTx, error) { + return c.env.UnconfirmedTx(ctx, &coretypes.RequestUnconfirmedTx{TxHash: txHash}) +} func (c *Local) NumUnconfirmedTxs(ctx context.Context) (*coretypes.ResultUnconfirmedTxs, error) { return c.env.NumUnconfirmedTxs(ctx) diff --git a/rpc/client/mocks/client.go b/rpc/client/mocks/client.go index b8577328f1..1b1aac0b9a 100644 --- a/rpc/client/mocks/client.go +++ b/rpc/client/mocks/client.go @@ -1911,9 +1911,68 @@ func (_c *Client_TxSearch_Call) RunAndReturn(run func(context.Context, string, b return _c } -// UnconfirmedTxs provides a mock function with given fields: ctx, page, perPage, txHash -func (_m *Client) UnconfirmedTxs(ctx context.Context, page *int, perPage *int, txHash []byte) (*coretypes.ResultUnconfirmedTxs, error) { - ret := _m.Called(ctx, page, perPage, txHash) +// UnconfirmedTx provides a mock function with given fields: ctx, txHash +func (_m *Client) UnconfirmedTx(ctx context.Context, txHash []byte) (*coretypes.ResultUnconfirmedTx, error) { + ret := _m.Called(ctx, txHash) + + if len(ret) == 0 { + panic("no return value specified for UnconfirmedTx") + } + + var r0 *coretypes.ResultUnconfirmedTx + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []byte) (*coretypes.ResultUnconfirmedTx, error)); ok { + return rf(ctx, txHash) + } + if rf, ok := ret.Get(0).(func(context.Context, []byte) *coretypes.ResultUnconfirmedTx); ok { + r0 = rf(ctx, txHash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*coretypes.ResultUnconfirmedTx) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, []byte) error); ok { + r1 = rf(ctx, txHash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Client_UnconfirmedTx_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnconfirmedTx' +type Client_UnconfirmedTx_Call struct { + *mock.Call +} + +// UnconfirmedTx is a helper method to define mock.On call +// - ctx context.Context +// - txHash []byte +func (_e *Client_Expecter) UnconfirmedTx(ctx interface{}, txHash interface{}) *Client_UnconfirmedTx_Call { + return &Client_UnconfirmedTx_Call{Call: _e.mock.On("UnconfirmedTx", ctx, txHash)} +} + +func (_c *Client_UnconfirmedTx_Call) Run(run func(ctx context.Context, txHash []byte)) *Client_UnconfirmedTx_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].([]byte)) + }) + return _c +} + +func (_c *Client_UnconfirmedTx_Call) Return(_a0 *coretypes.ResultUnconfirmedTx, _a1 error) *Client_UnconfirmedTx_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Client_UnconfirmedTx_Call) RunAndReturn(run func(context.Context, []byte) (*coretypes.ResultUnconfirmedTx, error)) *Client_UnconfirmedTx_Call { + _c.Call.Return(run) + return _c +} + +// UnconfirmedTxs provides a mock function with given fields: ctx, page, perPage +func (_m *Client) UnconfirmedTxs(ctx context.Context, page *int, perPage *int) (*coretypes.ResultUnconfirmedTxs, error) { + ret := _m.Called(ctx, page, perPage) if len(ret) == 0 { panic("no return value specified for UnconfirmedTxs") @@ -1921,19 +1980,19 @@ func (_m *Client) UnconfirmedTxs(ctx context.Context, page *int, perPage *int, t var r0 *coretypes.ResultUnconfirmedTxs var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *int, *int, []byte) (*coretypes.ResultUnconfirmedTxs, error)); ok { - return rf(ctx, page, perPage, txHash) + if rf, ok := ret.Get(0).(func(context.Context, *int, *int) (*coretypes.ResultUnconfirmedTxs, error)); ok { + return rf(ctx, page, perPage) } - if rf, ok := ret.Get(0).(func(context.Context, *int, *int, []byte) *coretypes.ResultUnconfirmedTxs); ok { - r0 = rf(ctx, page, perPage, txHash) + if rf, ok := ret.Get(0).(func(context.Context, *int, *int) *coretypes.ResultUnconfirmedTxs); ok { + r0 = rf(ctx, page, perPage) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*coretypes.ResultUnconfirmedTxs) } } - if rf, ok := ret.Get(1).(func(context.Context, *int, *int, []byte) error); ok { - r1 = rf(ctx, page, perPage, txHash) + if rf, ok := ret.Get(1).(func(context.Context, *int, *int) error); ok { + r1 = rf(ctx, page, perPage) } else { r1 = ret.Error(1) } @@ -1950,14 +2009,13 @@ type Client_UnconfirmedTxs_Call struct { // - ctx context.Context // - page *int // - perPage *int -// - txHash []byte -func (_e *Client_Expecter) UnconfirmedTxs(ctx interface{}, page interface{}, perPage interface{}, txHash interface{}) *Client_UnconfirmedTxs_Call { - return &Client_UnconfirmedTxs_Call{Call: _e.mock.On("UnconfirmedTxs", ctx, page, perPage, txHash)} +func (_e *Client_Expecter) UnconfirmedTxs(ctx interface{}, page interface{}, perPage interface{}) *Client_UnconfirmedTxs_Call { + return &Client_UnconfirmedTxs_Call{Call: _e.mock.On("UnconfirmedTxs", ctx, page, perPage)} } -func (_c *Client_UnconfirmedTxs_Call) Run(run func(ctx context.Context, page *int, perPage *int, txHash []byte)) *Client_UnconfirmedTxs_Call { +func (_c *Client_UnconfirmedTxs_Call) Run(run func(ctx context.Context, page *int, perPage *int)) *Client_UnconfirmedTxs_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*int), args[2].(*int), args[3].([]byte)) + run(args[0].(context.Context), args[1].(*int), args[2].(*int)) }) return _c } @@ -1967,7 +2025,7 @@ func (_c *Client_UnconfirmedTxs_Call) Return(_a0 *coretypes.ResultUnconfirmedTxs return _c } -func (_c *Client_UnconfirmedTxs_Call) RunAndReturn(run func(context.Context, *int, *int, []byte) (*coretypes.ResultUnconfirmedTxs, error)) *Client_UnconfirmedTxs_Call { +func (_c *Client_UnconfirmedTxs_Call) RunAndReturn(run func(context.Context, *int, *int) (*coretypes.ResultUnconfirmedTxs, error)) *Client_UnconfirmedTxs_Call { _c.Call.Return(run) return _c } diff --git a/rpc/client/mocks/remoteclient.go b/rpc/client/mocks/remoteclient.go index 74b8f0de5c..53822bc2e5 100644 --- a/rpc/client/mocks/remoteclient.go +++ b/rpc/client/mocks/remoteclient.go @@ -1956,9 +1956,68 @@ func (_c *RemoteClient_TxSearch_Call) RunAndReturn(run func(context.Context, str return _c } -// UnconfirmedTxs provides a mock function with given fields: ctx, page, perPage, txHash -func (_m *RemoteClient) UnconfirmedTxs(ctx context.Context, page *int, perPage *int, txHash []byte) (*coretypes.ResultUnconfirmedTxs, error) { - ret := _m.Called(ctx, page, perPage, txHash) +// UnconfirmedTx provides a mock function with given fields: ctx, txHash +func (_m *RemoteClient) UnconfirmedTx(ctx context.Context, txHash []byte) (*coretypes.ResultUnconfirmedTx, error) { + ret := _m.Called(ctx, txHash) + + if len(ret) == 0 { + panic("no return value specified for UnconfirmedTx") + } + + var r0 *coretypes.ResultUnconfirmedTx + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []byte) (*coretypes.ResultUnconfirmedTx, error)); ok { + return rf(ctx, txHash) + } + if rf, ok := ret.Get(0).(func(context.Context, []byte) *coretypes.ResultUnconfirmedTx); ok { + r0 = rf(ctx, txHash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*coretypes.ResultUnconfirmedTx) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, []byte) error); ok { + r1 = rf(ctx, txHash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RemoteClient_UnconfirmedTx_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnconfirmedTx' +type RemoteClient_UnconfirmedTx_Call struct { + *mock.Call +} + +// UnconfirmedTx is a helper method to define mock.On call +// - ctx context.Context +// - txHash []byte +func (_e *RemoteClient_Expecter) UnconfirmedTx(ctx interface{}, txHash interface{}) *RemoteClient_UnconfirmedTx_Call { + return &RemoteClient_UnconfirmedTx_Call{Call: _e.mock.On("UnconfirmedTx", ctx, txHash)} +} + +func (_c *RemoteClient_UnconfirmedTx_Call) Run(run func(ctx context.Context, txHash []byte)) *RemoteClient_UnconfirmedTx_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].([]byte)) + }) + return _c +} + +func (_c *RemoteClient_UnconfirmedTx_Call) Return(_a0 *coretypes.ResultUnconfirmedTx, _a1 error) *RemoteClient_UnconfirmedTx_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *RemoteClient_UnconfirmedTx_Call) RunAndReturn(run func(context.Context, []byte) (*coretypes.ResultUnconfirmedTx, error)) *RemoteClient_UnconfirmedTx_Call { + _c.Call.Return(run) + return _c +} + +// UnconfirmedTxs provides a mock function with given fields: ctx, page, perPage +func (_m *RemoteClient) UnconfirmedTxs(ctx context.Context, page *int, perPage *int) (*coretypes.ResultUnconfirmedTxs, error) { + ret := _m.Called(ctx, page, perPage) if len(ret) == 0 { panic("no return value specified for UnconfirmedTxs") @@ -1966,19 +2025,19 @@ func (_m *RemoteClient) UnconfirmedTxs(ctx context.Context, page *int, perPage * var r0 *coretypes.ResultUnconfirmedTxs var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *int, *int, []byte) (*coretypes.ResultUnconfirmedTxs, error)); ok { - return rf(ctx, page, perPage, txHash) + if rf, ok := ret.Get(0).(func(context.Context, *int, *int) (*coretypes.ResultUnconfirmedTxs, error)); ok { + return rf(ctx, page, perPage) } - if rf, ok := ret.Get(0).(func(context.Context, *int, *int, []byte) *coretypes.ResultUnconfirmedTxs); ok { - r0 = rf(ctx, page, perPage, txHash) + if rf, ok := ret.Get(0).(func(context.Context, *int, *int) *coretypes.ResultUnconfirmedTxs); ok { + r0 = rf(ctx, page, perPage) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*coretypes.ResultUnconfirmedTxs) } } - if rf, ok := ret.Get(1).(func(context.Context, *int, *int, []byte) error); ok { - r1 = rf(ctx, page, perPage, txHash) + if rf, ok := ret.Get(1).(func(context.Context, *int, *int) error); ok { + r1 = rf(ctx, page, perPage) } else { r1 = ret.Error(1) } @@ -1995,14 +2054,13 @@ type RemoteClient_UnconfirmedTxs_Call struct { // - ctx context.Context // - page *int // - perPage *int -// - txHash []byte -func (_e *RemoteClient_Expecter) UnconfirmedTxs(ctx interface{}, page interface{}, perPage interface{}, txHash interface{}) *RemoteClient_UnconfirmedTxs_Call { - return &RemoteClient_UnconfirmedTxs_Call{Call: _e.mock.On("UnconfirmedTxs", ctx, page, perPage, txHash)} +func (_e *RemoteClient_Expecter) UnconfirmedTxs(ctx interface{}, page interface{}, perPage interface{}) *RemoteClient_UnconfirmedTxs_Call { + return &RemoteClient_UnconfirmedTxs_Call{Call: _e.mock.On("UnconfirmedTxs", ctx, page, perPage)} } -func (_c *RemoteClient_UnconfirmedTxs_Call) Run(run func(ctx context.Context, page *int, perPage *int, txHash []byte)) *RemoteClient_UnconfirmedTxs_Call { +func (_c *RemoteClient_UnconfirmedTxs_Call) Run(run func(ctx context.Context, page *int, perPage *int)) *RemoteClient_UnconfirmedTxs_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*int), args[2].(*int), args[3].([]byte)) + run(args[0].(context.Context), args[1].(*int), args[2].(*int)) }) return _c } @@ -2012,7 +2070,7 @@ func (_c *RemoteClient_UnconfirmedTxs_Call) Return(_a0 *coretypes.ResultUnconfir return _c } -func (_c *RemoteClient_UnconfirmedTxs_Call) RunAndReturn(run func(context.Context, *int, *int, []byte) (*coretypes.ResultUnconfirmedTxs, error)) *RemoteClient_UnconfirmedTxs_Call { +func (_c *RemoteClient_UnconfirmedTxs_Call) RunAndReturn(run func(context.Context, *int, *int) (*coretypes.ResultUnconfirmedTxs, error)) *RemoteClient_UnconfirmedTxs_Call { _c.Call.Return(run) return _c } diff --git a/rpc/coretypes/requests.go b/rpc/coretypes/requests.go index b826906469..af6db924c9 100644 --- a/rpc/coretypes/requests.go +++ b/rpc/coretypes/requests.go @@ -76,9 +76,13 @@ type RequestConsensusParams struct { } type RequestUnconfirmedTxs struct { - Page *Int64 `json:"page"` - PerPage *Int64 `json:"per_page"` - TxHash bytes.HexBytes `json:"tx_hash,omitempty"` + Page *Int64 `json:"page"` + PerPage *Int64 `json:"per_page"` +} + +// RequestUnconfirmedTx is used to request a single unconfirmed transaction. +type RequestUnconfirmedTx struct { + TxHash bytes.HexBytes `json:"tx_hash,omitempty"` } type RequestBroadcastTx struct { diff --git a/rpc/coretypes/responses.go b/rpc/coretypes/responses.go index 329e1dc397..821a206431 100644 --- a/rpc/coretypes/responses.go +++ b/rpc/coretypes/responses.go @@ -312,6 +312,11 @@ type ResultUnconfirmedTxs struct { Txs []types.Tx `json:"txs"` } +// Fetch transaction from mempool +type ResultUnconfirmedTx struct { + Tx types.Tx `json:"txs"` +} + // Info abci msg type ResultABCIInfo struct { Response abci.ResponseInfo `json:"response"` diff --git a/types/tx.go b/types/tx.go index 27688ea9f5..6b69e931f8 100644 --- a/types/tx.go +++ b/types/tx.go @@ -2,7 +2,6 @@ package types import ( "bytes" - "crypto/sha256" "errors" "fmt" "sort" @@ -25,7 +24,11 @@ const maxLoggedTxs = 20 type Tx []byte // Key produces a fixed-length key for use in indexing. -func (tx Tx) Key() TxKey { return sha256.Sum256(tx) } +func (tx Tx) Key() TxKey { + // we must use the hash of the transaction as the key to + // make it easier to lookup transactions in the mempool + return TxKey(tx.Hash()) +} // Hash computes the TMHASH hash of the wire encoded transaction. func (tx Tx) Hash() tmbytes.HexBytes { return crypto.Checksum(tx) } From e3b6eda084b8e01d6817f5c12440b5cd0f8f80b8 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Fri, 21 Feb 2025 13:59:10 +0100 Subject: [PATCH 4/8] chore(rpc): unconfirmed tx route added --- internal/rpc/core/routes.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/rpc/core/routes.go b/internal/rpc/core/routes.go index 75b602ba9d..e0cc9a6e8c 100644 --- a/internal/rpc/core/routes.go +++ b/internal/rpc/core/routes.go @@ -56,6 +56,7 @@ func NewRoutesMap(svc RPCService, opts *RouteOptions) RoutesMap { "consensus_state": rpc.NewRPCFunc(svc.GetConsensusState), "consensus_params": rpc.NewRPCFunc(svc.ConsensusParams), "unconfirmed_txs": rpc.NewRPCFunc(svc.UnconfirmedTxs), + "unconfirmed_tx": rpc.NewRPCFunc(svc.UnconfirmedTx), "num_unconfirmed_txs": rpc.NewRPCFunc(svc.NumUnconfirmedTxs), // tx broadcast API From 925f8d2589825ea771d6db7a48c97c9fd75a90d8 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Fri, 21 Feb 2025 13:59:30 +0100 Subject: [PATCH 5/8] test(rpc): test unconfirmed txs --- rpc/client/rpc_test.go | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index 7544c9f846..3478b16cb3 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -663,6 +663,41 @@ func TestClientMethodCallsAdvanced(t *testing.T) { pool.Flush() }) + + t.Run("UnconfirmedTx", func(t *testing.T) { + // populate mempool with 5 tx + txs := make([]types.Tx, 5) + ch := make(chan error, 5) + for i := 0; i < 5; i++ { + _, _, tx := MakeTxKV() + + txs[i] = tx + err := pool.CheckTx(ctx, tx, func(_ *abci.ResponseCheckTx) { ch <- nil }, mempool.TxInfo{}) + + require.NoError(t, err) + } + // wait for tx to arrive in mempoool. + for i := 0; i < 5; i++ { + select { + case <-ch: + case <-time.After(5 * time.Second): + t.Error("Timed out waiting for CheckTx callback") + } + } + close(ch) + + for _, c := range GetClients(t, n, conf) { + for _, tx := range txs { + mc := c.(client.MempoolClient) + res, err := mc.UnconfirmedTx(ctx, tx.Hash()) + require.NoError(t, err) + + assert.Equal(t, tx, res.Tx) + } + } + + pool.Flush() + }) t.Run("NumUnconfirmedTxs", func(t *testing.T) { ch := make(chan struct{}) From c9ba1ef932c96425829ef1b2e42a132a7fdeae6f Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Fri, 21 Feb 2025 14:26:58 +0100 Subject: [PATCH 6/8] chore: improvements --- internal/rpc/core/doc.go | 1 + internal/rpc/core/mempool.go | 5 ++-- rpc/coretypes/requests.go | 2 +- rpc/coretypes/responses.go | 2 +- rpc/openapi/openapi.yaml | 54 +++++++++++++++++++++++++++++++++++- 5 files changed, 59 insertions(+), 5 deletions(-) diff --git a/internal/rpc/core/doc.go b/internal/rpc/core/doc.go index 77ace4e2cf..25cfe1365c 100644 --- a/internal/rpc/core/doc.go +++ b/internal/rpc/core/doc.go @@ -24,6 +24,7 @@ Available endpoints: /status /health /unconfirmed_txs +/unconfirmed_tx /unsafe_flush_mempool /validators diff --git a/internal/rpc/core/mempool.go b/internal/rpc/core/mempool.go index 1d7a9728cb..456f6d2f4b 100644 --- a/internal/rpc/core/mempool.go +++ b/internal/rpc/core/mempool.go @@ -8,6 +8,7 @@ import ( "time" abci "github.com/dashpay/tenderdash/abci/types" + "github.com/dashpay/tenderdash/crypto" "github.com/dashpay/tenderdash/internal/mempool" "github.com/dashpay/tenderdash/internal/state/indexer" tmmath "github.com/dashpay/tenderdash/libs/math" @@ -197,8 +198,8 @@ func (env *Environment) UnconfirmedTxs(_ctx context.Context, req *coretypes.Requ // return single unconfirmed transaction, matching req.TxHash func (env *Environment) UnconfirmedTx(_ctx context.Context, req *coretypes.RequestUnconfirmedTx) (*coretypes.ResultUnconfirmedTx, error) { - if req == nil || req.TxHash.IsZero() { - return nil, errors.New("you mustprovide transaction hash in tx_hash") + if req == nil || req.TxHash.IsZero() || len(req.TxHash) != crypto.HashSize { + return nil, errors.New("you must provide transaction hash in hash= argument") } tx := env.Mempool.GetTxByHash(types.TxKey(req.TxHash)) diff --git a/rpc/coretypes/requests.go b/rpc/coretypes/requests.go index af6db924c9..190601b989 100644 --- a/rpc/coretypes/requests.go +++ b/rpc/coretypes/requests.go @@ -82,7 +82,7 @@ type RequestUnconfirmedTxs struct { // RequestUnconfirmedTx is used to request a single unconfirmed transaction. type RequestUnconfirmedTx struct { - TxHash bytes.HexBytes `json:"tx_hash,omitempty"` + TxHash bytes.HexBytes `json:"hash,omitempty"` } type RequestBroadcastTx struct { diff --git a/rpc/coretypes/responses.go b/rpc/coretypes/responses.go index 821a206431..8444924bb9 100644 --- a/rpc/coretypes/responses.go +++ b/rpc/coretypes/responses.go @@ -314,7 +314,7 @@ type ResultUnconfirmedTxs struct { // Fetch transaction from mempool type ResultUnconfirmedTx struct { - Tx types.Tx `json:"txs"` + Tx types.Tx `json:"tx"` } // Info abci msg diff --git a/rpc/openapi/openapi.yaml b/rpc/openapi/openapi.yaml index bedb64d886..05321fd9d9 100644 --- a/rpc/openapi/openapi.yaml +++ b/rpc/openapi/openapi.yaml @@ -1107,7 +1107,6 @@ paths: schema: $ref: "#/components/schemas/ErrorResponse" - /dump_consensus_state: get: summary: Get consensus state @@ -1224,6 +1223,36 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" + /unconfirmed_tx: + get: + summary: Get unconfirmed transaction by hash + operationId: unconfirmed_tx + parameters: + - in: query + name: hash + description: "Transaction hash" + required: false + schema: + type: string + example: "0xD70952032620CC4E2737EB8AC379806359D8E0B17B0488F627997A0B043ABDED" + + tags: + - Info + description: | + Get unconfirmed transaction by hash + responses: + "200": + description: Transaction + content: + application/json: + schema: + $ref: "#/components/schemas/UnconfirmedTransactionResponse" + "500": + description: Error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" /num_unconfirmed_txs: get: summary: Get data about unconfirmed transactions @@ -2712,6 +2741,29 @@ components: - "gAPwYl3uCjCMTXENChSMnIkb5ZpYHBKIZqecFEV2tuZr7xIUA75/FmYq9WymsOBJ0XSJ8yV8zmQKMIxNcQ0KFIyciRvlmlgcEohmp5wURXa25mvvEhQbrvwbvlNiT+Yjr86G+YQNx7kRVgowjE1xDQoUjJyJG+WaWBwSiGannBRFdrbma+8SFK2m+1oxgILuQLO55n8mWfnbIzyPCjCMTXENChSMnIkb5ZpYHBKIZqecFEV2tuZr7xIUQNGfkmhTNMis4j+dyMDIWXdIPiYKMIxNcQ0KFIyciRvlmlgcEohmp5wURXa25mvvEhS8sL0D0wwgGCItQwVowak5YB38KRIUCg4KBXVhdG9tEgUxMDA1NBDoxRgaagom61rphyECn8x7emhhKdRCB2io7aS/6Cpuq5NbVqbODmqOT3jWw6kSQKUresk+d+Gw0BhjiggTsu8+1voW+VlDCQ1GRYnMaFOHXhyFv7BCLhFWxLxHSAYT8a5XqoMayosZf9mANKdXArA=" type: object + UnconfirmedTransactionResponse: + type: object + required: + - "jsonrpc" + - "id" + - "result" + properties: + jsonrpc: + type: string + example: "2.0" + id: + type: integer + example: 0 + result: + required: + - "tx" + properties: + tx: + type: string + example: "EjQSNFZ4kKvN7wmHZUMhAAA=" + description: Base-64 encoded transaction + type: object + TxSearchResponse: type: object required: From 3e1326c58cfd4a62c95e4a8a7285ed3d8fe18674 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Fri, 21 Feb 2025 17:16:44 +0100 Subject: [PATCH 7/8] chore: self-review --- internal/rpc/core/mempool.go | 3 +-- rpc/openapi/openapi.yaml | 9 ++------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/internal/rpc/core/mempool.go b/internal/rpc/core/mempool.go index 456f6d2f4b..844836a896 100644 --- a/internal/rpc/core/mempool.go +++ b/internal/rpc/core/mempool.go @@ -184,7 +184,6 @@ func (env *Environment) UnconfirmedTxs(_ctx context.Context, req *coretypes.Requ } skipCount := validateSkipCount(page, perPage) - // TODO: filter by tx hash here txs := env.Mempool.ReapMaxTxs(skipCount + tmmath.MinInt(perPage, totalCount-skipCount)) result := txs[skipCount:] @@ -199,7 +198,7 @@ func (env *Environment) UnconfirmedTxs(_ctx context.Context, req *coretypes.Requ // return single unconfirmed transaction, matching req.TxHash func (env *Environment) UnconfirmedTx(_ctx context.Context, req *coretypes.RequestUnconfirmedTx) (*coretypes.ResultUnconfirmedTx, error) { if req == nil || req.TxHash.IsZero() || len(req.TxHash) != crypto.HashSize { - return nil, errors.New("you must provide transaction hash in hash= argument") + return nil, fmt.Errorf("you must provide a valid %d-byte transaction hash in hash= argument", crypto.HashSize) } tx := env.Mempool.GetTxByHash(types.TxKey(req.TxHash)) diff --git a/rpc/openapi/openapi.yaml b/rpc/openapi/openapi.yaml index 05321fd9d9..a86b20a639 100644 --- a/rpc/openapi/openapi.yaml +++ b/rpc/openapi/openapi.yaml @@ -1,9 +1,6 @@ openapi: 3.0.0 info: - title: Tendermint RPC - contact: - name: Tendermint RPC - url: https://github.com/tendermint/tendermint/issues/new/choose + title: Tenderdash RPC description: | Tendermint supports the following RPC protocols: @@ -59,10 +56,8 @@ info: name: Apache 2.0 url: https://github.com/dashevo/tenderdash/blob/master/LICENSE servers: - - url: https://rpc.cosmos.network - description: Cosmos mainnet node to interact with the Tendermint RPC - url: http://localhost:26657 - description: Interact with the Tendermint RPC locally on your device + description: Interact with the Tenderdash RPC locally on your device tags: - name: Websocket description: Subscribe/unsubscribe are reserved for websocket events. From 0287b537682cf4899ba0f01298cc6f730b575447 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Mon, 24 Feb 2025 12:59:25 +0100 Subject: [PATCH 8/8] chore: unconfirmed_tx is required --- rpc/openapi/openapi.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc/openapi/openapi.yaml b/rpc/openapi/openapi.yaml index a86b20a639..b7a121ad6c 100644 --- a/rpc/openapi/openapi.yaml +++ b/rpc/openapi/openapi.yaml @@ -1226,7 +1226,7 @@ paths: - in: query name: hash description: "Transaction hash" - required: false + required: true schema: type: string example: "0xD70952032620CC4E2737EB8AC379806359D8E0B17B0488F627997A0B043ABDED"