diff --git a/node/node.go b/node/node.go index 7fe874c0..7b39189f 100644 --- a/node/node.go +++ b/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 diff --git a/rpc/client/httpclient.go b/rpc/client/httpclient.go index cc46cf69..04595e76 100644 --- a/rpc/client/httpclient.go +++ b/rpc/client/httpclient.go @@ -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 { diff --git a/rpc/client/interface.go b/rpc/client/interface.go index 26ce5242..2ba89079 100644 --- a/rpc/client/interface.go +++ b/rpc/client/interface.go @@ -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. diff --git a/rpc/client/localclient.go b/rpc/client/localclient.go index 41bb0c50..d0f0d11b 100644 --- a/rpc/client/localclient.go +++ b/rpc/client/localclient.go @@ -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) } diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index 9e254376..18f0f1aa 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -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) diff --git a/rpc/core/pipe.go b/rpc/core/pipe.go index c9221642..4993ed99 100644 --- a/rpc/core/pipe.go +++ b/rpc/core/pipe.go @@ -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 } diff --git a/rpc/core/routes.go b/rpc/core/routes.go index 4dc5aef8..38e60960 100644 --- a/rpc/core/routes.go +++ b/rpc/core/routes.go @@ -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) { diff --git a/rpc/core/tx.go b/rpc/core/tx.go index e7c416da..7f3cdd03 100644 --- a/rpc/core/tx.go +++ b/rpc/core/tx.go @@ -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 } diff --git a/rpc/test/client_test.go b/rpc/test/client_test.go index 8a41d397..50e32605 100644 --- a/rpc/test/client_test.go +++ b/rpc/test/client_test.go @@ -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 { diff --git a/state/execution.go b/state/execution.go index f6898cb4..05496aad 100644 --- a/state/execution.go +++ b/state/execution.go @@ -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 diff --git a/state/txindex/kv/kv_test.go b/state/txindex/kv/kv_test.go index 022e227e..7d4a84dd 100644 --- a/state/txindex/kv/kv_test.go +++ b/state/txindex/kv/kv_test.go @@ -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 { diff --git a/types/tx.go b/types/tx.go index 91f6fbc0..df7f0e71 100644 --- a/types/tx.go +++ b/types/tx.go @@ -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"` }