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