mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-26 11:11:41 +00:00
rpc: /tx allows height+hash
This commit is contained in:
@ -5,16 +5,30 @@ import (
|
|||||||
|
|
||||||
abci "github.com/tendermint/abci/types"
|
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/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Tx(hash []byte, height, index int, prove bool) (*ctypes.ResultTx, error) {
|
func Tx(hash []byte, height, index int, prove bool) (*ctypes.ResultTx, error) {
|
||||||
var deliverTx abci.ResponseDeliverTx
|
|
||||||
if len(hash) > 0 {
|
|
||||||
if height != 0 || index != 0 {
|
|
||||||
return nil, fmt.Errorf("Invalid args. If hash is provided, height and index should not be")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 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")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
r, err := txIndexer.Tx(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -26,19 +40,31 @@ func Tx(hash []byte, height, index int, prove bool) (*ctypes.ResultTx, error) {
|
|||||||
|
|
||||||
height = int(r.Height) // XXX
|
height = int(r.Height) // XXX
|
||||||
index = int(r.Index)
|
index = int(r.Index)
|
||||||
deliverTx = r.DeliverTx
|
txResult = r.DeliverTx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// height must be valid
|
||||||
if height <= 0 || height > blockStore.Height() {
|
if height <= 0 || height > blockStore.Height() {
|
||||||
return nil, fmt.Errorf("Invalid height (%d) for blockStore at height %d", height, blockStore.Height())
|
return nil, fmt.Errorf("Invalid height (%d) for blockStore at height %d", height, blockStore.Height())
|
||||||
}
|
}
|
||||||
|
|
||||||
block := blockStore.LoadBlock(height)
|
block := blockStore.LoadBlock(height)
|
||||||
|
|
||||||
|
// index must be valid
|
||||||
if index < 0 || index >= len(block.Data.Txs) {
|
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))
|
return nil, fmt.Errorf("Index (%d) is out of range for block (%d) with %d txs", index, height, len(block.Data.Txs))
|
||||||
}
|
}
|
||||||
tx := block.Data.Txs[index]
|
|
||||||
|
// 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 {
|
||||||
@ -46,10 +72,10 @@ func Tx(hash []byte, height, index int, prove bool) (*ctypes.ResultTx, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &ctypes.ResultTx{
|
return &ctypes.ResultTx{
|
||||||
Height: height,
|
Height: height,
|
||||||
Index: index,
|
Index: index,
|
||||||
DeliverTx: deliverTx,
|
TxResult: txResult,
|
||||||
Tx: tx,
|
Tx: tx,
|
||||||
Proof: proof,
|
Proof: proof,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -94,11 +94,11 @@ type ResultBroadcastTxCommit struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ResultTx struct {
|
type ResultTx struct {
|
||||||
Height int `json:"height"`
|
Height int `json:"height"`
|
||||||
Index int `json:"index"`
|
Index int `json:"index"`
|
||||||
DeliverTx abci.ResponseDeliverTx `json:"deliver_tx"`
|
TxResult abci.ResponseDeliverTx `json:"tx_result"`
|
||||||
Tx types.Tx `json:"tx"`
|
Tx types.Tx `json:"tx"`
|
||||||
Proof types.TxProof `json:"proof,omitempty"`
|
Proof types.TxProof `json:"proof,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResultUnconfirmedTxs struct {
|
type ResultUnconfirmedTxs struct {
|
||||||
|
@ -165,8 +165,9 @@ func testTx(t *testing.T, client rpc.HTTPClient) {
|
|||||||
|
|
||||||
// first we broadcast a tx
|
// first we broadcast a tx
|
||||||
tmResult := new(ctypes.TMResult)
|
tmResult := new(ctypes.TMResult)
|
||||||
tx := types.Tx(randBytes(t))
|
txBytes := randBytes(t)
|
||||||
_, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, tmResult)
|
tx := types.Tx(txBytes)
|
||||||
|
_, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": txBytes}, tmResult)
|
||||||
require.Nil(err)
|
require.Nil(err)
|
||||||
|
|
||||||
res := (*tmResult).(*ctypes.ResultBroadcastTxCommit)
|
res := (*tmResult).(*ctypes.ResultBroadcastTxCommit)
|
||||||
@ -195,10 +196,10 @@ func testTx(t *testing.T, client rpc.HTTPClient) {
|
|||||||
// on proper hash match
|
// on proper hash match
|
||||||
{true, 0, 0, tx.Hash(), false},
|
{true, 0, 0, tx.Hash(), false},
|
||||||
{true, 0, 0, tx.Hash(), true},
|
{true, 0, 0, tx.Hash(), true},
|
||||||
{false, res.Height, 0, tx.Hash(), false}, // TODO: or shall we allow this????
|
{true, res.Height, 0, tx.Hash(), false},
|
||||||
{false, res.Height, 0, tx.Hash(), true}, // TODO: or shall we allow this????
|
{true, res.Height, 0, tx.Hash(), true},
|
||||||
|
{true, 100, 0, tx.Hash(), false}, // with indexer disabled, height is overwritten
|
||||||
// with extra data is an error
|
// with extra data is an error
|
||||||
{false, 10, 0, tx.Hash(), false},
|
|
||||||
{false, 0, 2, tx.Hash(), true},
|
{false, 0, 2, tx.Hash(), true},
|
||||||
{false, 0, 0, []byte("jkh8y0fw"), false},
|
{false, 0, 0, []byte("jkh8y0fw"), false},
|
||||||
{false, 0, 0, nil, true},
|
{false, 0, 0, nil, true},
|
||||||
@ -229,7 +230,7 @@ func testTx(t *testing.T, client rpc.HTTPClient) {
|
|||||||
assert.Equal(tx, res2.Tx, idx)
|
assert.Equal(tx, res2.Tx, idx)
|
||||||
assert.Equal(res.Height, res2.Height, idx)
|
assert.Equal(res.Height, res2.Height, idx)
|
||||||
assert.Equal(0, res2.Index, idx)
|
assert.Equal(0, res2.Index, idx)
|
||||||
assert.Equal(abci.CodeType_OK, res2.DeliverTx.Code, idx)
|
assert.Equal(abci.CodeType_OK, res2.TxResult.Code, idx)
|
||||||
// time to verify the proof
|
// time to verify the proof
|
||||||
proof := res2.Proof
|
proof := res2.Proof
|
||||||
if tc.prove && assert.Equal(tx, proof.Data, idx) {
|
if tc.prove && assert.Equal(tx, proof.Data, idx) {
|
||||||
|
10
types/tx.go
10
types/tx.go
@ -45,6 +45,16 @@ func (txs Txs) Index(tx Tx) int {
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Index returns the index of this transaction hash in the list, or -1 if not found
|
||||||
|
func (txs Txs) IndexByHash(hash []byte) int {
|
||||||
|
for i := range txs {
|
||||||
|
if bytes.Equal(txs[i].Hash(), hash) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
// Proof returns a simple merkle proof for this node.
|
// Proof returns a simple merkle proof for this node.
|
||||||
//
|
//
|
||||||
// Panics if i < 0 or i >= len(txs)
|
// Panics if i < 0 or i >= len(txs)
|
||||||
|
Reference in New Issue
Block a user