Browse Source

rpc: add /check_tx endpoint (#5017)

Closes #4549
pull/5033/head
Anton Kaliaev 4 years ago
committed by GitHub
parent
commit
257a374b78
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 164 additions and 4 deletions
  1. +1
    -0
      CHANGELOG_PENDING.md
  2. +4
    -0
      light/rpc/client.go
  3. +2
    -2
      mempool/clist_mempool.go
  4. +2
    -1
      node/node.go
  5. +5
    -0
      proxy/app_conn.go
  6. +9
    -0
      rpc/client/http/http.go
  7. +1
    -0
      rpc/client/interface.go
  8. +4
    -0
      rpc/client/local/local.go
  9. +4
    -0
      rpc/client/mock/client.go
  10. +14
    -0
      rpc/client/rpc_test.go
  11. +2
    -1
      rpc/core/env.go
  12. +11
    -0
      rpc/core/mempool.go
  13. +1
    -0
      rpc/core/routes.go
  14. +5
    -0
      rpc/core/types/responses.go
  15. +99
    -0
      rpc/swagger/swagger.yaml

+ 1
- 0
CHANGELOG_PENDING.md View File

@ -87,6 +87,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi
- [rpc] \#4979 Support EXISTS operator in `/tx_search` query (@melekes)
- [p2p] \#4981 Expose `SaveAs` func on NodeKey (@melekes)
- [evidence] [#4821](https://github.com/tendermint/tendermint/pull/4821) Amnesia evidence can be detected, verified and committed (@cmwaters)
- [rpc] \#5017 Add `/check_tx` endpoint to check transactions without executing them or adding them to the mempool (@melekes)
### IMPROVEMENTS:


+ 4
- 0
light/rpc/client.go View File

@ -144,6 +144,10 @@ func (c *Client) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) {
return c.next.NumUnconfirmedTxs()
}
func (c *Client) CheckTx(tx types.Tx) (*ctypes.ResultCheckTx, error) {
return c.next.CheckTx(tx)
}
func (c *Client) NetInfo() (*ctypes.ResultNetInfo, error) {
return c.next.NetInfo()
}


+ 2
- 2
mempool/clist_mempool.go View File

@ -706,9 +706,9 @@ func (cache *mapTxCache) Push(tx types.Tx) bool {
if cache.list.Len() >= cache.size {
popped := cache.list.Front()
poppedTxHash := popped.Value.([sha256.Size]byte) //nolint:staticcheck // SA5011: possible nil pointer dereference
delete(cache.cacheMap, poppedTxHash)
if popped != nil {
poppedTxHash := popped.Value.([sha256.Size]byte)
delete(cache.cacheMap, poppedTxHash)
cache.list.Remove(popped)
}
}


+ 2
- 1
node/node.go View File

@ -958,7 +958,8 @@ func (n *Node) ConfigureRPC() error {
return fmt.Errorf("can't get pubkey: %w", err)
}
rpccore.SetEnvironment(&rpccore.Environment{
ProxyAppQuery: n.proxyApp.Query(),
ProxyAppQuery: n.proxyApp.Query(),
ProxyAppMempool: n.proxyApp.Mempool(),
StateDB: n.stateDB,
BlockStore: n.blockStore,


+ 5
- 0
proxy/app_conn.go View File

@ -27,6 +27,7 @@ type AppConnMempool interface {
Error() error
CheckTxAsync(types.RequestCheckTx) *abcicli.ReqRes
CheckTxSync(types.RequestCheckTx) (*types.ResponseCheckTx, error)
FlushAsync() *abcicli.ReqRes
FlushSync() error
@ -125,6 +126,10 @@ func (app *appConnMempool) CheckTxAsync(req types.RequestCheckTx) *abcicli.ReqRe
return app.appConn.CheckTxAsync(req)
}
func (app *appConnMempool) CheckTxSync(req types.RequestCheckTx) (*types.ResponseCheckTx, error) {
return app.appConn.CheckTxSync(req)
}
//------------------------------------------------
// Implements AppConnQuery (subset of abcicli.Client)


+ 9
- 0
rpc/client/http/http.go View File

@ -288,6 +288,15 @@ func (c *baseRPCClient) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error
return result, nil
}
func (c *baseRPCClient) CheckTx(tx types.Tx) (*ctypes.ResultCheckTx, error) {
result := new(ctypes.ResultCheckTx)
_, err := c.caller.Call("check_tx", map[string]interface{}{"tx": tx}, result)
if err != nil {
return nil, err
}
return result, nil
}
func (c *baseRPCClient) NetInfo() (*ctypes.ResultNetInfo, error) {
result := new(ctypes.ResultNetInfo)
_, err := c.caller.Call("net_info", map[string]interface{}{}, result)


+ 1
- 0
rpc/client/interface.go View File

@ -115,6 +115,7 @@ type EventsClient interface {
type MempoolClient interface {
UnconfirmedTxs(limit *int) (*ctypes.ResultUnconfirmedTxs, error)
NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error)
CheckTx(tx types.Tx) (*ctypes.ResultCheckTx, error)
}
// EvidenceClient is used for submitting an evidence of the malicious


+ 4
- 0
rpc/client/local/local.go View File

@ -104,6 +104,10 @@ func (c *Local) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) {
return core.NumUnconfirmedTxs(c.ctx)
}
func (c *Local) CheckTx(tx types.Tx) (*ctypes.ResultCheckTx, error) {
return core.CheckTx(c.ctx, tx)
}
func (c *Local) NetInfo() (*ctypes.ResultNetInfo, error) {
return core.NetInfo(c.ctx)
}


+ 4
- 0
rpc/client/mock/client.go View File

@ -110,6 +110,10 @@ func (c Client) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error)
return core.BroadcastTxSync(&rpctypes.Context{}, tx)
}
func (c Client) CheckTx(tx types.Tx) (*ctypes.ResultCheckTx, error) {
return core.CheckTx(&rpctypes.Context{}, tx)
}
func (c Client) NetInfo() (*ctypes.ResultNetInfo, error) {
return core.NetInfo(&rpctypes.Context{})
}


+ 14
- 0
rpc/client/rpc_test.go View File

@ -392,6 +392,20 @@ func TestNumUnconfirmedTxs(t *testing.T) {
mempool.Flush()
}
func TestCheckTx(t *testing.T) {
mempool := node.Mempool()
for _, c := range GetClients() {
_, _, tx := MakeTxKV()
res, err := c.CheckTx(tx)
require.NoError(t, err)
assert.Equal(t, abci.CodeTypeOK, res.Code)
assert.Equal(t, 0, mempool.Size(), "mempool must be empty")
}
}
func TestTx(t *testing.T) {
// first we broadcast a tx
c := getHTTPClient()


+ 2
- 1
rpc/core/env.go View File

@ -67,7 +67,8 @@ type peers interface {
// to be setup once during startup.
type Environment struct {
// external, thread safe interfaces
ProxyAppQuery proxy.AppConnQuery
ProxyAppQuery proxy.AppConnQuery
ProxyAppMempool proxy.AppConnMempool
// interfaces defined in types and above
StateDB dbm.DB


+ 11
- 0
rpc/core/mempool.go View File

@ -150,3 +150,14 @@ func NumUnconfirmedTxs(ctx *rpctypes.Context) (*ctypes.ResultUnconfirmedTxs, err
Total: env.Mempool.Size(),
TotalBytes: env.Mempool.TxsBytes()}, nil
}
// CheckTx checks the transaction without executing it. The transaction won't
// be added to the mempool either.
// More: https://docs.tendermint.com/master/rpc/#/Tx/check_tx
func CheckTx(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultCheckTx, error) {
res, err := env.ProxyAppMempool.CheckTxSync(abci.RequestCheckTx{Tx: tx})
if err != nil {
return nil, err
}
return &ctypes.ResultCheckTx{ResponseCheckTx: *res}, nil
}

+ 1
- 0
rpc/core/routes.go View File

@ -22,6 +22,7 @@ var Routes = map[string]*rpc.RPCFunc{
"block_by_hash": rpc.NewRPCFunc(BlockByHash, "hash"),
"block_results": rpc.NewRPCFunc(BlockResults, "height"),
"commit": rpc.NewRPCFunc(Commit, "height"),
"check_tx": rpc.NewRPCFunc(CheckTx, "tx"),
"tx": rpc.NewRPCFunc(Tx, "hash,prove"),
"tx_search": rpc.NewRPCFunc(TxSearch, "query,prove,page,per_page,order_by"),
"validators": rpc.NewRPCFunc(Validators, "height,page,per_page"),


+ 5
- 0
rpc/core/types/responses.go View File

@ -174,6 +174,11 @@ type ResultBroadcastTxCommit struct {
Height int64 `json:"height"`
}
// ResultCheckTx wraps abci.ResponseCheckTx.
type ResultCheckTx struct {
abci.ResponseCheckTx
}
// Result of querying for a tx
type ResultTx struct {
Hash bytes.HexBytes `json:"hash"`


+ 99
- 0
rpc/swagger/swagger.yaml View File

@ -153,6 +153,39 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/check_tx:
get:
summary: Checks the transaction without executing it.
tags:
- Tx
operationId: broadcast_tx_commit
description: |
The transaction won't be added to the mempool.
Please refer to
https://docs.tendermint.com/master/tendermint-core/using-tendermint.html#formatting
for formatting/encoding rules.
parameters:
- in: query
name: tx
required: true
schema:
type: string
example: "785"
description: The transaction
responses:
200:
description: ABCI application's CheckTx response
content:
application/json:
schema:
$ref: "#/components/schemas/CheckTxResponse"
500:
description: empty error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/subscribe:
get:
summary: Subscribe for events via WebSocket.
@ -2923,6 +2956,72 @@ components:
jsonrpc:
type: "string"
example: "2.0"
CheckTxResponse:
type: object
required:
- "error"
- "result"
- "id"
- "jsonrpc"
properties:
error:
type: "string"
example: ""
result:
required:
- "log"
- "data"
- "code"
properties:
code:
type: "string"
example: "0"
data:
type: "string"
example: ""
log:
type: "string"
example: ""
info:
type: "string"
example: ""
gas_wanted:
type: "string"
example: "1"
gas_used:
type: "string"
example: "0"
events:
type: "array"
x-nullable: true
items:
type: "object"
properties:
type:
type: "string"
example: "app"
attributes:
type: "array"
x-nullable: false
items:
type: "object"
properties:
key:
type: "string"
example: "Y3JlYXRvcg=="
value:
type: "string"
example: "Q29zbW9zaGkgTmV0b3dva28="
codespace:
type: "string"
example: "bank"
type: "object"
id:
type: "number"
example: 0
jsonrpc:
type: "string"
example: "2.0"
BroadcastTxResponse:
type: object
required:


Loading…
Cancel
Save