TxResult includes Tx. /tx only works if indexer active
This commit is contained in:
parent
d572bb0c5d
commit
f4d0076344
13
node/node.go
13
node/node.go
|
@ -23,8 +23,9 @@ import (
|
|||
rpccore "github.com/tendermint/tendermint/rpc/core"
|
||||
grpccore "github.com/tendermint/tendermint/rpc/grpc"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/state/tx"
|
||||
txindexer "github.com/tendermint/tendermint/state/tx/indexer"
|
||||
"github.com/tendermint/tendermint/state/txindex"
|
||||
"github.com/tendermint/tendermint/state/txindex/kv"
|
||||
"github.com/tendermint/tendermint/state/txindex/null"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
"github.com/tendermint/tendermint/version"
|
||||
|
||||
|
@ -53,7 +54,7 @@ type Node struct {
|
|||
consensusReactor *consensus.ConsensusReactor // for participating in the consensus
|
||||
proxyApp proxy.AppConns // connection to the application
|
||||
rpcListeners []net.Listener // rpc servers
|
||||
txIndexer tx.Indexer
|
||||
txIndexer txindex.TxIndexer
|
||||
}
|
||||
|
||||
func NewNodeDefault(config cfg.Config) *Node {
|
||||
|
@ -88,13 +89,13 @@ func NewNode(config cfg.Config, privValidator *types.PrivValidator, clientCreato
|
|||
state = sm.LoadState(stateDB)
|
||||
|
||||
// Transaction indexing
|
||||
var txIndexer tx.Indexer
|
||||
var txIndexer txindex.TxIndexer
|
||||
switch config.GetString("tx_indexer") {
|
||||
case "kv":
|
||||
store := dbm.NewDB("tx_indexer", config.GetString("db_backend"), config.GetString("db_dir"))
|
||||
txIndexer = txindexer.NewKV(store)
|
||||
txIndexer = kv.NewTxIndex(store)
|
||||
default:
|
||||
txIndexer = &txindexer.Null{}
|
||||
txIndexer = &null.TxIndex{}
|
||||
}
|
||||
state.TxIndexer = txIndexer
|
||||
|
||||
|
|
|
@ -160,13 +160,11 @@ func (c *HTTP) Commit(height int) (*ctypes.ResultCommit, error) {
|
|||
return (*tmResult).(*ctypes.ResultCommit), nil
|
||||
}
|
||||
|
||||
func (c *HTTP) Tx(hash []byte, height, index int, prove bool) (*ctypes.ResultTx, error) {
|
||||
func (c *HTTP) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
|
||||
tmResult := new(ctypes.TMResult)
|
||||
query := map[string]interface{}{
|
||||
"height": height,
|
||||
"index": index,
|
||||
"hash": hash,
|
||||
"prove": prove,
|
||||
"hash": hash,
|
||||
"prove": prove,
|
||||
}
|
||||
_, err := c.rpc.Call("tx", query, tmResult)
|
||||
if err != nil {
|
||||
|
|
|
@ -44,7 +44,7 @@ type SignClient interface {
|
|||
Block(height int) (*ctypes.ResultBlock, error)
|
||||
Commit(height int) (*ctypes.ResultCommit, error)
|
||||
Validators() (*ctypes.ResultValidators, error)
|
||||
Tx(hash []byte, height, index int, prove bool) (*ctypes.ResultTx, error)
|
||||
Tx(hash []byte, prove bool) (*ctypes.ResultTx, error)
|
||||
}
|
||||
|
||||
// HistoryClient shows us data from genesis to now in large chunks.
|
||||
|
|
|
@ -104,6 +104,6 @@ func (c Local) Validators() (*ctypes.ResultValidators, error) {
|
|||
return core.Validators()
|
||||
}
|
||||
|
||||
func (c Local) Tx(hash []byte, height, index int, prove bool) (*ctypes.ResultTx, error) {
|
||||
return core.Tx(hash, height, index, prove)
|
||||
func (c Local) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
|
||||
return core.Tx(hash, prove)
|
||||
}
|
||||
|
|
|
@ -133,8 +133,8 @@ func TestAppCalls(t *testing.T) {
|
|||
}
|
||||
|
||||
// make sure we can lookup the tx with proof
|
||||
// ptx, err := c.Tx(bres.TxID, txh, 0, true)
|
||||
ptx, err := c.Tx(bres.TxID, 0, 0, true)
|
||||
// ptx, err := c.Tx(bres.TxID, true)
|
||||
ptx, err := c.Tx(bres.TxID, true)
|
||||
require.Nil(err, "%d: %+v", i, err)
|
||||
assert.Equal(txh, ptx.Height)
|
||||
assert.Equal(types.Tx(tx), ptx.Tx)
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
p2p "github.com/tendermint/go-p2p"
|
||||
"github.com/tendermint/tendermint/consensus"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
"github.com/tendermint/tendermint/state/tx"
|
||||
"github.com/tendermint/tendermint/state/txindex"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
|
@ -46,7 +46,7 @@ var (
|
|||
pubKey crypto.PubKey
|
||||
genDoc *types.GenesisDoc // cache the genesis structure
|
||||
addrBook *p2p.AddrBook
|
||||
txIndexer tx.Indexer
|
||||
txIndexer txindex.TxIndexer
|
||||
)
|
||||
|
||||
func SetConfig(c cfg.Config) {
|
||||
|
@ -89,6 +89,6 @@ func SetProxyAppQuery(appConn proxy.AppConnQuery) {
|
|||
proxyAppQuery = appConn
|
||||
}
|
||||
|
||||
func SetTxIndexer(indexer tx.Indexer) {
|
||||
func SetTxIndexer(indexer txindex.TxIndexer) {
|
||||
txIndexer = indexer
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ var Routes = map[string]*rpc.RPCFunc{
|
|||
"genesis": rpc.NewRPCFunc(GenesisResult, ""),
|
||||
"block": rpc.NewRPCFunc(BlockResult, "height"),
|
||||
"commit": rpc.NewRPCFunc(CommitResult, "height"),
|
||||
"tx": rpc.NewRPCFunc(TxResult, "hash,height,index,prove"),
|
||||
"tx": rpc.NewRPCFunc(TxResult, "hash,prove"),
|
||||
"validators": rpc.NewRPCFunc(ValidatorsResult, ""),
|
||||
"dump_consensus_state": rpc.NewRPCFunc(DumpConsensusStateResult, ""),
|
||||
"unconfirmed_txs": rpc.NewRPCFunc(UnconfirmedTxsResult, ""),
|
||||
|
@ -100,8 +100,8 @@ func NumUnconfirmedTxsResult() (ctypes.TMResult, error) {
|
|||
// Tx allow user to query the transaction results. `nil` could mean the
|
||||
// transaction is in the mempool, invalidated, or was not send in the first
|
||||
// place.
|
||||
func TxResult(hash []byte, height, index int, prove bool) (ctypes.TMResult, error) {
|
||||
return Tx(hash, height, index, prove)
|
||||
func TxResult(hash []byte, prove bool) (ctypes.TMResult, error) {
|
||||
return Tx(hash, prove)
|
||||
}
|
||||
|
||||
func BroadcastTxCommitResult(tx []byte) (ctypes.TMResult, error) {
|
||||
|
|
|
@ -3,79 +3,41 @@ package core
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
"github.com/tendermint/tendermint/state/tx/indexer"
|
||||
"github.com/tendermint/tendermint/state/txindex/null"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
func Tx(hash []byte, height, index int, prove bool) (*ctypes.ResultTx, error) {
|
||||
func Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
|
||||
|
||||
// if index is disabled, we need a height
|
||||
_, indexerDisabled := txIndexer.(*indexer.Null)
|
||||
if indexerDisabled && height == 0 {
|
||||
return nil, fmt.Errorf("TxIndexer is disabled. Please specify a height to search for the tx by hash or index")
|
||||
// if index is disabled, return error
|
||||
if _, ok := txIndexer.(*null.TxIndex); ok {
|
||||
return nil, fmt.Errorf("Transaction indexing is disabled.")
|
||||
}
|
||||
|
||||
// hash and index must not be passed together
|
||||
if len(hash) > 0 && index != 0 {
|
||||
return nil, fmt.Errorf("Invalid args. Only one of hash and index may be provided")
|
||||
r, err := txIndexer.Get(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// results
|
||||
var txResult abci.ResponseDeliverTx
|
||||
var tx types.Tx
|
||||
|
||||
// if indexer is enabled and we have a hash,
|
||||
// fetch the tx result and set the height and index
|
||||
if !indexerDisabled && len(hash) > 0 {
|
||||
r, err := txIndexer.Tx(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if r == nil {
|
||||
return &ctypes.ResultTx{}, fmt.Errorf("Tx (%X) not found", hash)
|
||||
}
|
||||
|
||||
height = int(r.Height) // XXX
|
||||
index = int(r.Index)
|
||||
txResult = r.DeliverTx
|
||||
if r == nil {
|
||||
return nil, fmt.Errorf("Tx (%X) not found", hash)
|
||||
}
|
||||
|
||||
// height must be valid
|
||||
if height <= 0 || height > blockStore.Height() {
|
||||
return nil, fmt.Errorf("Invalid height (%d) for blockStore at height %d", height, blockStore.Height())
|
||||
}
|
||||
|
||||
block := blockStore.LoadBlock(height)
|
||||
|
||||
// index must be valid
|
||||
if index < 0 || index >= len(block.Data.Txs) {
|
||||
return nil, fmt.Errorf("Index (%d) is out of range for block (%d) with %d txs", index, height, len(block.Data.Txs))
|
||||
}
|
||||
|
||||
// if indexer is disabled and we have a hash,
|
||||
// search for it in the list of txs
|
||||
if indexerDisabled && len(hash) > 0 {
|
||||
index = block.Data.Txs.IndexByHash(hash)
|
||||
if index < 0 {
|
||||
return nil, fmt.Errorf("Tx hash %X not found in block %d", hash, height)
|
||||
}
|
||||
|
||||
}
|
||||
tx = block.Data.Txs[index]
|
||||
height := int(r.Height) // XXX
|
||||
index := int(r.Index)
|
||||
|
||||
var proof types.TxProof
|
||||
if prove {
|
||||
block := blockStore.LoadBlock(height)
|
||||
proof = block.Data.Txs.Proof(index)
|
||||
}
|
||||
|
||||
return &ctypes.ResultTx{
|
||||
Height: height,
|
||||
Index: index,
|
||||
TxResult: txResult,
|
||||
Tx: tx,
|
||||
TxResult: r.Result,
|
||||
Tx: r.Tx,
|
||||
Proof: proof,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ import (
|
|||
rpc "github.com/tendermint/go-rpc/client"
|
||||
"github.com/tendermint/tendermint/rpc/core"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
"github.com/tendermint/tendermint/state/tx/indexer"
|
||||
"github.com/tendermint/tendermint/state/txindex/null"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
|
@ -157,7 +157,7 @@ func testBroadcastTxCommit(t *testing.T, client rpc.HTTPClient) {
|
|||
func TestURITx(t *testing.T) {
|
||||
testTx(t, GetURIClient(), true)
|
||||
|
||||
core.SetTxIndexer(&indexer.Null{})
|
||||
core.SetTxIndexer(&null.TxIndex{})
|
||||
testTx(t, GetJSONClient(), false)
|
||||
core.SetTxIndexer(node.ConsensusState().GetState().TxIndexer)
|
||||
}
|
||||
|
@ -165,7 +165,7 @@ func TestURITx(t *testing.T) {
|
|||
func TestJSONTx(t *testing.T) {
|
||||
testTx(t, GetJSONClient(), true)
|
||||
|
||||
core.SetTxIndexer(&indexer.Null{})
|
||||
core.SetTxIndexer(&null.TxIndex{})
|
||||
testTx(t, GetJSONClient(), false)
|
||||
core.SetTxIndexer(node.ConsensusState().GetState().TxIndexer)
|
||||
}
|
||||
|
@ -188,36 +188,21 @@ func testTx(t *testing.T, client rpc.HTTPClient, withIndexer bool) {
|
|||
mem := node.MempoolReactor().Mempool
|
||||
require.Equal(0, mem.Size())
|
||||
|
||||
txHash := tx.Hash()
|
||||
txHash2 := types.Tx("a different tx").Hash()
|
||||
|
||||
cases := []struct {
|
||||
validWithIndexer bool
|
||||
validNoIndexer bool
|
||||
height int
|
||||
index int
|
||||
hash []byte
|
||||
prove bool
|
||||
valid bool
|
||||
hash []byte
|
||||
prove bool
|
||||
}{
|
||||
// only on proper height, index match
|
||||
{true, true, res.Height, 0, nil, false},
|
||||
{true, true, res.Height, 0, nil, true},
|
||||
{false, false, res.Height, 1, nil, false},
|
||||
{false, false, res.Height, -7, nil, true},
|
||||
{false, false, -10, -100, nil, false},
|
||||
{false, false, res.Height + 1, 0, nil, true},
|
||||
|
||||
// on proper hash match
|
||||
{true, false, 0, 0, tx.Hash(), false},
|
||||
{true, false, 0, 0, tx.Hash(), true},
|
||||
{true, true, res.Height, 0, tx.Hash(), false},
|
||||
{true, true, res.Height, 0, tx.Hash(), true},
|
||||
{true, false, 100, 0, tx.Hash(), false}, // with indexer enabled, height is overwritten
|
||||
// with extra data is an error
|
||||
{false, false, 0, 2, tx.Hash(), true},
|
||||
{false, false, 0, 0, []byte("jkh8y0fw"), false},
|
||||
{false, false, 0, 0, nil, true},
|
||||
|
||||
// missing height and hash fails
|
||||
{false, false, 0, 0, nil, false},
|
||||
{false, false, 0, 1, nil, true},
|
||||
// only valid if correct hash provided
|
||||
{true, txHash, false},
|
||||
{true, txHash, true},
|
||||
{false, txHash2, false},
|
||||
{false, txHash2, true},
|
||||
{false, nil, false},
|
||||
{false, nil, true},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
|
@ -227,13 +212,11 @@ func testTx(t *testing.T, client rpc.HTTPClient, withIndexer bool) {
|
|||
// since there's only one tx, we know index=0.
|
||||
tmResult = new(ctypes.TMResult)
|
||||
query := map[string]interface{}{
|
||||
"height": tc.height,
|
||||
"index": tc.index,
|
||||
"hash": tc.hash,
|
||||
"prove": tc.prove,
|
||||
"hash": tc.hash,
|
||||
"prove": tc.prove,
|
||||
}
|
||||
_, err = client.Call("tx", query, tmResult)
|
||||
valid := (withIndexer && tc.validWithIndexer) || (!withIndexer && tc.validNoIndexer)
|
||||
valid := (withIndexer && tc.valid)
|
||||
if !valid {
|
||||
require.NotNil(err, idx)
|
||||
} else {
|
||||
|
|
|
@ -84,7 +84,12 @@ func execBlockOnProxyApp(eventCache types.Fireable, proxyAppConn proxy.AppConnCo
|
|||
txError = txResult.Code.String()
|
||||
}
|
||||
|
||||
txResults[txIndex] = &types.TxResult{uint64(block.Height), uint32(txIndex), *txResult}
|
||||
txResults[txIndex] = &types.TxResult{
|
||||
Height: uint64(block.Height),
|
||||
Index: uint32(txIndex),
|
||||
Tx: req.GetDeliverTx().Tx,
|
||||
Result: *txResult,
|
||||
}
|
||||
txIndex++
|
||||
|
||||
// NOTE: if we count we can access the tx from the block instead of
|
||||
|
|
|
@ -18,7 +18,7 @@ func TestTxIndex(t *testing.T) {
|
|||
indexer := &TxIndex{store: db.NewMemDB()}
|
||||
|
||||
tx := types.Tx("HELLO WORLD")
|
||||
txResult := &types.TxResult{1, 1, abci.ResponseDeliverTx{Data: []byte{0}, Code: abci.CodeType_OK, Log: ""}}
|
||||
txResult := &types.TxResult{1, 1, tx, abci.ResponseDeliverTx{Data: []byte{0}, Code: abci.CodeType_OK, Log: ""}}
|
||||
hash := tx.Hash()
|
||||
|
||||
batch := txindex.NewBatch()
|
||||
|
@ -32,7 +32,8 @@ func TestTxIndex(t *testing.T) {
|
|||
}
|
||||
|
||||
func benchmarkTxIndex(txsCount int, b *testing.B) {
|
||||
txResult := &types.TxResult{1, 1, abci.ResponseDeliverTx{Data: []byte{0}, Code: abci.CodeType_OK, Log: ""}}
|
||||
tx := types.Tx("HELLO WORLD")
|
||||
txResult := &types.TxResult{1, 1, tx, abci.ResponseDeliverTx{Data: []byte{0}, Code: abci.CodeType_OK, Log: ""}}
|
||||
|
||||
dir, err := ioutil.TempDir("", "tx_indexer_db")
|
||||
if err != nil {
|
||||
|
|
|
@ -106,7 +106,8 @@ func (tp TxProof) Validate(dataHash []byte) error {
|
|||
//
|
||||
// One usage is indexing transaction results.
|
||||
type TxResult struct {
|
||||
Height uint64 `json:"height"`
|
||||
Index uint32 `json:"index"`
|
||||
DeliverTx abci.ResponseDeliverTx `json:"deliver_tx"`
|
||||
Height uint64 `json:"height"`
|
||||
Index uint32 `json:"index"`
|
||||
Tx Tx `json:"tx"`
|
||||
Result abci.ResponseDeliverTx `json:"result"`
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue