From bdb34f9f4e81d99d9308cb930b9cbad195e7ce7d Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 27 Apr 2017 19:01:18 -0400 Subject: [PATCH 01/23] types: []byte -> data.Bytes --- types/block.go | 23 ++++++++++++----------- types/canonical_json.go | 10 +++++++--- types/events.go | 9 +++++---- types/genesis.go | 7 ++++--- types/part_set.go | 27 ++++++++++++++------------- types/tx.go | 3 ++- types/tx_test.go | 6 +++--- types/validator.go | 13 +++++++------ types/vote.go | 11 ++++++----- 9 files changed, 60 insertions(+), 49 deletions(-) diff --git a/types/block.go b/types/block.go index 88288a2bd..65d4031da 100644 --- a/types/block.go +++ b/types/block.go @@ -8,9 +8,10 @@ import ( "strings" "time" + "github.com/tendermint/go-wire" + "github.com/tendermint/go-wire/data" . "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/merkle" - "github.com/tendermint/go-wire" ) const MaxBlockSize = 22020096 // 21MB TODO make it configurable @@ -150,15 +151,15 @@ func (b *Block) StringShort() string { //----------------------------------------------------------------------------- type Header struct { - ChainID string `json:"chain_id"` - Height int `json:"height"` - Time time.Time `json:"time"` - NumTxs int `json:"num_txs"` // XXX: Can we get rid of this? - LastBlockID BlockID `json:"last_block_id"` - LastCommitHash []byte `json:"last_commit_hash"` // commit from validators from the last block - DataHash []byte `json:"data_hash"` // transactions - ValidatorsHash []byte `json:"validators_hash"` // validators for the current block - AppHash []byte `json:"app_hash"` // state after txs from the previous block + ChainID string `json:"chain_id"` + Height int `json:"height"` + Time time.Time `json:"time"` + NumTxs int `json:"num_txs"` // XXX: Can we get rid of this? + LastBlockID BlockID `json:"last_block_id"` + LastCommitHash data.Bytes `json:"last_commit_hash"` // commit from validators from the last block + DataHash data.Bytes `json:"data_hash"` // transactions + ValidatorsHash data.Bytes `json:"validators_hash"` // validators for the current block + AppHash data.Bytes `json:"app_hash"` // state after txs from the previous block } // NOTE: hash is nil if required fields are missing. @@ -388,7 +389,7 @@ func (data *Data) StringIndented(indent string) string { //-------------------------------------------------------------------------------- type BlockID struct { - Hash []byte `json:"hash"` + Hash data.Bytes `json:"hash"` PartsHeader PartSetHeader `json:"parts"` } diff --git a/types/canonical_json.go b/types/canonical_json.go index 68dd6924c..2e8583a4a 100644 --- a/types/canonical_json.go +++ b/types/canonical_json.go @@ -1,15 +1,19 @@ package types +import ( + "github.com/tendermint/go-wire/data" +) + // canonical json is go-wire's json for structs with fields in alphabetical order type CanonicalJSONBlockID struct { - Hash []byte `json:"hash,omitempty"` + Hash data.Bytes `json:"hash,omitempty"` PartsHeader CanonicalJSONPartSetHeader `json:"parts,omitempty"` } type CanonicalJSONPartSetHeader struct { - Hash []byte `json:"hash"` - Total int `json:"total"` + Hash data.Bytes `json:"hash"` + Total int `json:"total"` } type CanonicalJSONProposal struct { diff --git a/types/events.go b/types/events.go index 17f6fc6e4..953859d0d 100644 --- a/types/events.go +++ b/types/events.go @@ -3,9 +3,10 @@ package types import ( // for registering TMEventData as events.EventData abci "github.com/tendermint/abci/types" - . "github.com/tendermint/tmlibs/common" - "github.com/tendermint/tmlibs/events" "github.com/tendermint/go-wire" + "github.com/tendermint/go-wire/data" + cmn "github.com/tendermint/tmlibs/common" + "github.com/tendermint/tmlibs/events" ) // Functions to generate eventId strings @@ -16,7 +17,7 @@ func EventStringUnbond() string { return "Unbond" } func EventStringRebond() string { return "Rebond" } func EventStringDupeout() string { return "Dupeout" } func EventStringFork() string { return "Fork" } -func EventStringTx(tx Tx) string { return Fmt("Tx:%X", tx.Hash()) } +func EventStringTx(tx Tx) string { return cmn.Fmt("Tx:%X", tx.Hash()) } func EventStringNewBlock() string { return "NewBlock" } func EventStringNewBlockHeader() string { return "NewBlockHeader" } @@ -75,7 +76,7 @@ type EventDataNewBlockHeader struct { type EventDataTx struct { Height int `json:"height"` Tx Tx `json:"tx"` - Data []byte `json:"data"` + Data data.Bytes `json:"data"` Log string `json:"log"` Code abci.CodeType `json:"code"` Error string `json:"error"` // this is redundant information for now diff --git a/types/genesis.go b/types/genesis.go index 6a5006648..75999f631 100644 --- a/types/genesis.go +++ b/types/genesis.go @@ -4,8 +4,9 @@ import ( "encoding/json" "time" - . "github.com/tendermint/tmlibs/common" "github.com/tendermint/go-crypto" + "github.com/tendermint/go-wire/data" + cmn "github.com/tendermint/tmlibs/common" ) //------------------------------------------------------------ @@ -26,7 +27,7 @@ type GenesisDoc struct { GenesisTime time.Time `json:"genesis_time"` ChainID string `json:"chain_id"` Validators []GenesisValidator `json:"validators"` - AppHash []byte `json:"app_hash"` + AppHash data.Bytes `json:"app_hash"` } // Utility method for saving GenensisDoc as JSON file. @@ -35,7 +36,7 @@ func (genDoc *GenesisDoc) SaveAs(file string) error { if err != nil { return err } - return WriteFile(file, genDocBytes, 0644) + return cmn.WriteFile(file, genDocBytes, 0644) } //------------------------------------------------------------ diff --git a/types/part_set.go b/types/part_set.go index 96907aa5f..e15d2cab6 100644 --- a/types/part_set.go +++ b/types/part_set.go @@ -9,9 +9,10 @@ import ( "golang.org/x/crypto/ripemd160" - . "github.com/tendermint/tmlibs/common" - "github.com/tendermint/tmlibs/merkle" "github.com/tendermint/go-wire" + "github.com/tendermint/go-wire/data" + cmn "github.com/tendermint/tmlibs/common" + "github.com/tendermint/tmlibs/merkle" ) var ( @@ -21,7 +22,7 @@ var ( type Part struct { Index int `json:"index"` - Bytes []byte `json:"bytes"` + Bytes data.Bytes `json:"bytes"` Proof merkle.SimpleProof `json:"proof"` // Cache @@ -49,7 +50,7 @@ func (part *Part) StringIndented(indent string) string { %s Proof: %v %s}`, part.Index, - indent, Fingerprint(part.Bytes), + indent, cmn.Fingerprint(part.Bytes), indent, part.Proof.StringIndented(indent+" "), indent) } @@ -57,12 +58,12 @@ func (part *Part) StringIndented(indent string) string { //------------------------------------- type PartSetHeader struct { - Total int `json:"total"` - Hash []byte `json:"hash"` + Total int `json:"total"` + Hash data.Bytes `json:"hash"` } func (psh PartSetHeader) String() string { - return fmt.Sprintf("%v:%X", psh.Total, Fingerprint(psh.Hash)) + return fmt.Sprintf("%v:%X", psh.Total, cmn.Fingerprint(psh.Hash)) } func (psh PartSetHeader) IsZero() bool { @@ -85,7 +86,7 @@ type PartSet struct { mtx sync.Mutex parts []*Part - partsBitArray *BitArray + partsBitArray *cmn.BitArray count int } @@ -96,11 +97,11 @@ func NewPartSetFromData(data []byte, partSize int) *PartSet { total := (len(data) + partSize - 1) / partSize parts := make([]*Part, total) parts_ := make([]merkle.Hashable, total) - partsBitArray := NewBitArray(total) + partsBitArray := cmn.NewBitArray(total) for i := 0; i < total; i++ { part := &Part{ Index: i, - Bytes: data[i*partSize : MinInt(len(data), (i+1)*partSize)], + Bytes: data[i*partSize : cmn.MinInt(len(data), (i+1)*partSize)], } parts[i] = part parts_[i] = part @@ -126,7 +127,7 @@ func NewPartSetFromHeader(header PartSetHeader) *PartSet { total: header.Total, hash: header.Hash, parts: make([]*Part, header.Total), - partsBitArray: NewBitArray(header.Total), + partsBitArray: cmn.NewBitArray(header.Total), count: 0, } } @@ -150,7 +151,7 @@ func (ps *PartSet) HasHeader(header PartSetHeader) bool { } } -func (ps *PartSet) BitArray() *BitArray { +func (ps *PartSet) BitArray() *cmn.BitArray { ps.mtx.Lock() defer ps.mtx.Unlock() return ps.partsBitArray.Copy() @@ -224,7 +225,7 @@ func (ps *PartSet) IsComplete() bool { func (ps *PartSet) GetReader() io.Reader { if !ps.IsComplete() { - PanicSanity("Cannot GetReader() on incomplete PartSet") + cmn.PanicSanity("Cannot GetReader() on incomplete PartSet") } return NewPartSetReader(ps.parts) } diff --git a/types/tx.go b/types/tx.go index e62b5f666..ac3ec01d6 100644 --- a/types/tx.go +++ b/types/tx.go @@ -5,6 +5,7 @@ import ( "errors" abci "github.com/tendermint/abci/types" + "github.com/tendermint/go-wire/data" "github.com/tendermint/tmlibs/merkle" ) @@ -79,7 +80,7 @@ func (txs Txs) Proof(i int) TxProof { type TxProof struct { Index, Total int - RootHash []byte + RootHash data.Bytes Data Tx Proof merkle.SimpleProof } diff --git a/types/tx_test.go b/types/tx_test.go index 3ed59e3cb..91cddecfe 100644 --- a/types/tx_test.go +++ b/types/tx_test.go @@ -60,9 +60,9 @@ func TestValidTxProof(t *testing.T) { proof := txs.Proof(i) assert.Equal(i, proof.Index, "%d: %d", h, i) assert.Equal(len(txs), proof.Total, "%d: %d", h, i) - assert.Equal(root, proof.RootHash, "%d: %d", h, i) - assert.Equal(leaf, proof.Data, "%d: %d", h, i) - assert.Equal(leafHash, proof.LeafHash(), "%d: %d", h, i) + assert.EqualValues(root, proof.RootHash, "%d: %d", h, i) + assert.EqualValues(leaf, proof.Data, "%d: %d", h, i) + assert.EqualValues(leafHash, proof.LeafHash(), "%d: %d", h, i) assert.Nil(proof.Validate(root), "%d: %d", h, i) assert.NotNil(proof.Validate([]byte("foobar")), "%d: %d", h, i) diff --git a/types/validator.go b/types/validator.go index 2a8795bf3..595c52ff1 100644 --- a/types/validator.go +++ b/types/validator.go @@ -5,16 +5,17 @@ import ( "fmt" "io" - . "github.com/tendermint/tmlibs/common" "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire" + "github.com/tendermint/go-wire/data" + cmn "github.com/tendermint/tmlibs/common" ) // Volatile state for each Validator // TODO: make non-volatile identity // - Remove Accum - it can be computed, and now valset becomes identifying type Validator struct { - Address []byte `json:"address"` + Address data.Bytes `json:"address"` PubKey crypto.PubKey `json:"pub_key"` VotingPower int64 `json:"voting_power"` Accum int64 `json:"accum"` @@ -51,7 +52,7 @@ func (v *Validator) CompareAccum(other *Validator) *Validator { } else if bytes.Compare(v.Address, other.Address) > 0 { return other } else { - PanicSanity("Cannot compare identical validators") + cmn.PanicSanity("Cannot compare identical validators") return nil } } @@ -87,7 +88,7 @@ func (vc validatorCodec) Decode(r io.Reader, n *int, err *error) interface{} { } func (vc validatorCodec) Compare(o1 interface{}, o2 interface{}) int { - PanicSanity("ValidatorCodec.Compare not implemented") + cmn.PanicSanity("ValidatorCodec.Compare not implemented") return 0 } @@ -96,11 +97,11 @@ func (vc validatorCodec) Compare(o1 interface{}, o2 interface{}) int { func RandValidator(randPower bool, minPower int64) (*Validator, *PrivValidator) { privVal := GenPrivValidator() - _, tempFilePath := Tempfile("priv_validator_") + _, tempFilePath := cmn.Tempfile("priv_validator_") privVal.SetFile(tempFilePath) votePower := minPower if randPower { - votePower += int64(RandUint32()) + votePower += int64(cmn.RandUint32()) } val := NewValidator(privVal.PubKey, votePower) return val, privVal diff --git a/types/vote.go b/types/vote.go index 2ad9df0ad..164293c53 100644 --- a/types/vote.go +++ b/types/vote.go @@ -5,9 +5,10 @@ import ( "fmt" "io" - . "github.com/tendermint/tmlibs/common" "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire" + "github.com/tendermint/go-wire/data" + cmn "github.com/tendermint/tmlibs/common" ) var ( @@ -47,7 +48,7 @@ func IsVoteTypeValid(type_ byte) bool { // Represents a prevote, precommit, or commit vote from validators for consensus. type Vote struct { - ValidatorAddress []byte `json:"validator_address"` + ValidatorAddress data.Bytes `json:"validator_address"` ValidatorIndex int `json:"validator_index"` Height int `json:"height"` Round int `json:"round"` @@ -79,11 +80,11 @@ func (vote *Vote) String() string { case VoteTypePrecommit: typeString = "Precommit" default: - PanicSanity("Unknown vote type") + cmn.PanicSanity("Unknown vote type") } return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %v}", - vote.ValidatorIndex, Fingerprint(vote.ValidatorAddress), + vote.ValidatorIndex, cmn.Fingerprint(vote.ValidatorAddress), vote.Height, vote.Round, vote.Type, typeString, - Fingerprint(vote.BlockID.Hash), vote.Signature) + cmn.Fingerprint(vote.BlockID.Hash), vote.Signature) } From cdf650fba9fb8e277e06cb0d15ed63f25cfb8cf4 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 27 Apr 2017 19:06:07 -0400 Subject: [PATCH 02/23] rpc: repsonse types use data.Bytes --- rpc/core/types/responses.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rpc/core/types/responses.go b/rpc/core/types/responses.go index 469e71061..b4b52c9e5 100644 --- a/rpc/core/types/responses.go +++ b/rpc/core/types/responses.go @@ -5,10 +5,10 @@ import ( abci "github.com/tendermint/abci/types" "github.com/tendermint/go-crypto" - data "github.com/tendermint/go-wire/data" + "github.com/tendermint/go-wire" + "github.com/tendermint/go-wire/data" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/rpc/lib/types" - "github.com/tendermint/go-wire" "github.com/tendermint/tendermint/types" ) @@ -85,13 +85,13 @@ type ResultBroadcastTx struct { Data data.Bytes `json:"data"` Log string `json:"log"` - Hash []byte `json:"hash"` + Hash data.Bytes `json:"hash"` } type ResultBroadcastTxCommit struct { CheckTx *abci.ResponseCheckTx `json:"check_tx"` DeliverTx *abci.ResponseDeliverTx `json:"deliver_tx"` - Hash []byte `json:"hash"` + Hash data.Bytes `json:"hash"` Height int `json:"height"` } From a518d08839cc429bed1626fadd4eb101d744d73a Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 27 Apr 2017 19:34:25 -0400 Subject: [PATCH 03/23] rpc: response types use Result instead of pb Response --- rpc/core/abci.go | 10 +++++++++- rpc/core/mempool.go | 12 ++++++------ rpc/core/tx.go | 2 +- rpc/core/types/responses.go | 26 ++++++++++++++++---------- rpc/grpc/api.go | 16 +++++++++++++++- 5 files changed, 47 insertions(+), 19 deletions(-) diff --git a/rpc/core/abci.go b/rpc/core/abci.go index 5c9fcfc25..4b210ff13 100644 --- a/rpc/core/abci.go +++ b/rpc/core/abci.go @@ -18,7 +18,15 @@ func ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.ResultABCIQuer return nil, err } log.Info("ABCIQuery", "path", path, "data", data, "result", resQuery) - return &ctypes.ResultABCIQuery{resQuery}, nil + return &ctypes.ResultABCIQuery{ + Code: resQuery.Code, + Index: resQuery.Index, + Key: resQuery.Key, + Value: resQuery.Value, + Proof: resQuery.Proof, + Height: resQuery.Height, + Log: resQuery.Log, + }, nil } func ABCIInfo() (*ctypes.ResultABCIInfo, error) { diff --git a/rpc/core/mempool.go b/rpc/core/mempool.go index 4adf75ce5..4c1b54cfc 100644 --- a/rpc/core/mempool.go +++ b/rpc/core/mempool.go @@ -67,8 +67,8 @@ func BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { if checkTxR.Code != abci.CodeType_OK { // CheckTx failed! return &ctypes.ResultBroadcastTxCommit{ - CheckTx: checkTxR, - DeliverTx: nil, + CheckTx: checkTxR.Result(), + DeliverTx: abci.Result{}, Hash: tx.Hash(), }, nil } @@ -87,16 +87,16 @@ func BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { } log.Notice("DeliverTx passed ", "tx", data.Bytes(tx), "response", deliverTxR) return &ctypes.ResultBroadcastTxCommit{ - CheckTx: checkTxR, - DeliverTx: deliverTxR, + CheckTx: checkTxR.Result(), + DeliverTx: deliverTxR.Result(), Hash: tx.Hash(), Height: deliverTxRes.Height, }, nil case <-timer.C: log.Error("failed to include tx") return &ctypes.ResultBroadcastTxCommit{ - CheckTx: checkTxR, - DeliverTx: nil, + CheckTx: checkTxR.Result(), + DeliverTx: abci.Result{}, Hash: tx.Hash(), }, fmt.Errorf("Timed out waiting for transaction to be included in a block") } diff --git a/rpc/core/tx.go b/rpc/core/tx.go index 7f3cdd037..387d4afa6 100644 --- a/rpc/core/tx.go +++ b/rpc/core/tx.go @@ -36,7 +36,7 @@ func Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) { return &ctypes.ResultTx{ Height: height, Index: index, - TxResult: r.Result, + TxResult: r.Result.Result(), Tx: r.Tx, Proof: proof, }, nil diff --git a/rpc/core/types/responses.go b/rpc/core/types/responses.go index b4b52c9e5..367c82d23 100644 --- a/rpc/core/types/responses.go +++ b/rpc/core/types/responses.go @@ -89,18 +89,18 @@ type ResultBroadcastTx struct { } type ResultBroadcastTxCommit struct { - CheckTx *abci.ResponseCheckTx `json:"check_tx"` - DeliverTx *abci.ResponseDeliverTx `json:"deliver_tx"` - Hash data.Bytes `json:"hash"` - Height int `json:"height"` + CheckTx abci.Result `json:"check_tx"` + DeliverTx abci.Result `json:"deliver_tx"` + Hash data.Bytes `json:"hash"` + Height int `json:"height"` } type ResultTx struct { - Height int `json:"height"` - Index int `json:"index"` - TxResult abci.ResponseDeliverTx `json:"tx_result"` - Tx types.Tx `json:"tx"` - Proof types.TxProof `json:"proof,omitempty"` + Height int `json:"height"` + Index int `json:"index"` + TxResult abci.Result `json:"tx_result"` + Tx types.Tx `json:"tx"` + Proof types.TxProof `json:"proof,omitempty"` } type ResultUnconfirmedTxs struct { @@ -113,7 +113,13 @@ type ResultABCIInfo struct { } type ResultABCIQuery struct { - Response abci.ResponseQuery `json:"response"` + Code abci.CodeType `json:"code"` + Index int64 `json:"index"` + Key []byte `json:"key"` + Value []byte `json:"value"` + Proof []byte `json:"proof"` + Height uint64 `json:"height"` + Log string `json:"log"` } type ResultUnsafeFlushMempool struct{} diff --git a/rpc/grpc/api.go b/rpc/grpc/api.go index fab811c2e..7cfda1587 100644 --- a/rpc/grpc/api.go +++ b/rpc/grpc/api.go @@ -3,6 +3,8 @@ package core_grpc import ( core "github.com/tendermint/tendermint/rpc/core" + abci "github.com/tendermint/abci/types" + context "golang.org/x/net/context" ) @@ -14,5 +16,17 @@ func (bapi *broadcastAPI) BroadcastTx(ctx context.Context, req *RequestBroadcast if err != nil { return nil, err } - return &ResponseBroadcastTx{res.CheckTx, res.DeliverTx}, nil + return &ResponseBroadcastTx{ + + CheckTx: &abci.ResponseCheckTx{ + Code: res.CheckTx.Code, + Data: res.CheckTx.Data, + Log: res.CheckTx.Log, + }, + DeliverTx: &abci.ResponseDeliverTx{ + Code: res.DeliverTx.Code, + Data: res.DeliverTx.Data, + Log: res.DeliverTx.Log, + }, + }, nil } From c930f43cbe03fdc9a6b1018bdd5d002f095bd1b0 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 27 Apr 2017 19:51:18 -0400 Subject: [PATCH 04/23] rpc: fix tests --- rpc/client/mock/abci.go | 13 ++++++------- rpc/client/mock/abci_test.go | 12 ++++++------ rpc/client/rpc_test.go | 20 +++++++++----------- rpc/core/abci.go | 8 +------- rpc/core/types/responses.go | 8 +------- rpc/test/client_test.go | 10 +++++----- 6 files changed, 28 insertions(+), 43 deletions(-) diff --git a/rpc/client/mock/abci.go b/rpc/client/mock/abci.go index db9bd2c2b..0d1012557 100644 --- a/rpc/client/mock/abci.go +++ b/rpc/client/mock/abci.go @@ -25,18 +25,16 @@ func (a ABCIApp) ABCIInfo() (*ctypes.ResultABCIInfo, error) { func (a ABCIApp) ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.ResultABCIQuery, error) { q := a.App.Query(abci.RequestQuery{data, path, 0, prove}) - return &ctypes.ResultABCIQuery{q}, nil + return &ctypes.ResultABCIQuery{q.Result()}, nil } func (a ABCIApp) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { res := ctypes.ResultBroadcastTxCommit{} - c := a.App.CheckTx(tx) - res.CheckTx = &abci.ResponseCheckTx{c.Code, c.Data, c.Log} - if !c.IsOK() { + res.CheckTx = a.App.CheckTx(tx) + if !res.CheckTx.IsOK() { return &res, nil } - d := a.App.DeliverTx(tx) - res.DeliverTx = &abci.ResponseDeliverTx{d.Code, d.Data, d.Log} + res.DeliverTx = a.App.DeliverTx(tx) return &res, nil } @@ -85,7 +83,8 @@ func (m ABCIMock) ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.R if err != nil { return nil, err } - return &ctypes.ResultABCIQuery{res.(abci.ResponseQuery)}, nil + resQuery := res.(abci.ResponseQuery) + return &ctypes.ResultABCIQuery{resQuery.Result()}, nil } func (m ABCIMock) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { diff --git a/rpc/client/mock/abci_test.go b/rpc/client/mock/abci_test.go index 57334f25f..935f9ff94 100644 --- a/rpc/client/mock/abci_test.go +++ b/rpc/client/mock/abci_test.go @@ -36,8 +36,8 @@ func TestABCIMock(t *testing.T) { BroadcastCommit: mock.Call{ Args: goodTx, Response: &ctypes.ResultBroadcastTxCommit{ - CheckTx: &abci.ResponseCheckTx{Data: data.Bytes("stand")}, - DeliverTx: &abci.ResponseDeliverTx{Data: data.Bytes("deliver")}, + CheckTx: abci.Result{Data: data.Bytes("stand")}, + DeliverTx: abci.Result{Data: data.Bytes("deliver")}, }, Error: errors.New("bad tx"), }, @@ -53,9 +53,9 @@ func TestABCIMock(t *testing.T) { query, err := m.ABCIQuery("/", nil, false) require.Nil(err) require.NotNil(query) - assert.Equal(key, query.Response.GetKey()) - assert.Equal(value, query.Response.GetValue()) - assert.Equal(height, query.Response.GetHeight()) + assert.EqualValues(key, query.Key) + assert.EqualValues(value, query.Value) + assert.Equal(height, query.Height) // non-commit calls always return errors _, err = m.BroadcastTxSync(goodTx) @@ -166,5 +166,5 @@ func TestABCIApp(t *testing.T) { // check the key qres, err := m.ABCIQuery("/key", data.Bytes(key), false) require.Nil(err) - assert.EqualValues(value, qres.Response.Value) + assert.EqualValues(value, qres.Value) } diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index d0cb7a47b..4c21a4afc 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -10,7 +10,6 @@ import ( merktest "github.com/tendermint/merkleeyes/testutil" "github.com/tendermint/tendermint/rpc/client" rpctest "github.com/tendermint/tendermint/rpc/test" - "github.com/tendermint/tendermint/types" ) func getHTTPClient() *client.HTTP { @@ -119,17 +118,16 @@ func TestAppCalls(t *testing.T) { k, v, tx := merktest.MakeTxKV() bres, err := c.BroadcastTxCommit(tx) require.Nil(err, "%d: %+v", i, err) - require.True(bres.DeliverTx.GetCode().IsOK()) + require.True(bres.DeliverTx.Code.IsOK()) txh := bres.Height apph := txh + 1 // this is where the tx will be applied to the state // wait before querying client.WaitForHeight(c, apph, nil) qres, err := c.ABCIQuery("/key", k, false) - if assert.Nil(err) && assert.True(qres.Response.Code.IsOK()) { - data := qres.Response + if assert.Nil(err) && assert.True(qres.Code.IsOK()) { // assert.Equal(k, data.GetKey()) // only returned for proofs - assert.Equal(v, data.GetValue()) + assert.EqualValues(v, qres.Value) } // make sure we can lookup the tx with proof @@ -137,7 +135,7 @@ func TestAppCalls(t *testing.T) { ptx, err := c.Tx(bres.Hash, true) require.Nil(err, "%d: %+v", i, err) assert.Equal(txh, ptx.Height) - assert.Equal(types.Tx(tx), ptx.Tx) + assert.EqualValues(tx, ptx.Tx) // and we can even check the block is added block, err := c.Block(apph) @@ -174,12 +172,12 @@ func TestAppCalls(t *testing.T) { // and we got a proof that works! pres, err := c.ABCIQuery("/key", k, true) - if assert.Nil(err) && assert.True(pres.Response.Code.IsOK()) { - proof, err := iavl.ReadProof(pres.Response.GetProof()) + if assert.Nil(err) && assert.True(pres.Code.IsOK()) { + proof, err := iavl.ReadProof(pres.Proof) if assert.Nil(err) { - key := pres.Response.GetKey() - value := pres.Response.GetValue() - assert.Equal(appHash, proof.RootHash) + key := pres.Key + value := pres.Value + assert.EqualValues(appHash, proof.RootHash) valid := proof.Verify(key, value, appHash) assert.True(valid) } diff --git a/rpc/core/abci.go b/rpc/core/abci.go index 4b210ff13..169447526 100644 --- a/rpc/core/abci.go +++ b/rpc/core/abci.go @@ -19,13 +19,7 @@ func ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.ResultABCIQuer } log.Info("ABCIQuery", "path", path, "data", data, "result", resQuery) return &ctypes.ResultABCIQuery{ - Code: resQuery.Code, - Index: resQuery.Index, - Key: resQuery.Key, - Value: resQuery.Value, - Proof: resQuery.Proof, - Height: resQuery.Height, - Log: resQuery.Log, + resQuery.Result(), }, nil } diff --git a/rpc/core/types/responses.go b/rpc/core/types/responses.go index 367c82d23..ae2340a78 100644 --- a/rpc/core/types/responses.go +++ b/rpc/core/types/responses.go @@ -113,13 +113,7 @@ type ResultABCIInfo struct { } type ResultABCIQuery struct { - Code abci.CodeType `json:"code"` - Index int64 `json:"index"` - Key []byte `json:"key"` - Value []byte `json:"value"` - Proof []byte `json:"proof"` - Height uint64 `json:"height"` - Log string `json:"log"` + *abci.ResultQuery `json:"response"` } type ResultUnsafeFlushMempool struct{} diff --git a/rpc/test/client_test.go b/rpc/test/client_test.go index 67c88356d..6d0905fde 100644 --- a/rpc/test/client_test.go +++ b/rpc/test/client_test.go @@ -12,9 +12,9 @@ import ( "github.com/stretchr/testify/require" abci "github.com/tendermint/abci/types" - rpc "github.com/tendermint/tendermint/rpc/lib/client" "github.com/tendermint/tendermint/rpc/core" ctypes "github.com/tendermint/tendermint/rpc/core/types" + rpc "github.com/tendermint/tendermint/rpc/lib/client" "github.com/tendermint/tendermint/state/txindex/null" "github.com/tendermint/tendermint/types" . "github.com/tendermint/tmlibs/common" @@ -98,8 +98,8 @@ func sendTx(t *testing.T, client rpc.HTTPClient) ([]byte, []byte) { require.Nil(t, err) bres := (*tmResult).(*ctypes.ResultBroadcastTxCommit) require.NotNil(t, 0, bres.DeliverTx, "%#v", bres) - require.EqualValues(t, 0, bres.CheckTx.GetCode(), "%#v", bres) - require.EqualValues(t, 0, bres.DeliverTx.GetCode(), "%#v", bres) + require.EqualValues(t, 0, bres.CheckTx.Code, "%#v", bres) + require.EqualValues(t, 0, bres.DeliverTx.Code, "%#v", bres) return k, v } @@ -120,10 +120,10 @@ func testABCIQuery(t *testing.T, client rpc.HTTPClient) { require.Nil(t, err) resQuery := (*tmResult).(*ctypes.ResultABCIQuery) - require.EqualValues(t, 0, resQuery.Response.Code) + require.EqualValues(t, 0, resQuery.Code) // XXX: specific to value returned by the dummy - require.NotEqual(t, 0, len(resQuery.Response.Value)) + require.NotEqual(t, 0, len(resQuery.Value)) } //-------------------------------------------------------------------------------- From 2bf7e9c968f24c611e5663085d158b8c5ddabbc9 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 27 Apr 2017 19:58:12 -0400 Subject: [PATCH 05/23] update glide --- glide.lock | 45 +++++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/glide.lock b/glide.lock index 7720c946b..24d44a848 100644 --- a/glide.lock +++ b/glide.lock @@ -1,8 +1,8 @@ hash: 6f8962f6ca0e25b8e43bc6e496bd46c9ff3a79dcf789578efeeaee2fffc39c6e -updated: 2017-04-25T14:48:36.999444976-04:00 +updated: 2017-04-27T19:56:37.342860938-04:00 imports: - name: github.com/btcsuite/btcd - version: 583684b21bfbde9b5fc4403916fd7c807feb0289 + version: 4b348c1d33373d672edd83fc576892d0e46686d2 subpackages: - btcec - name: github.com/davecgh/go-spew @@ -20,11 +20,12 @@ imports: subpackages: - proto - name: github.com/golang/protobuf - version: 8ee79997227bf9b34611aee7946ae64735e6fd93 + version: 2bba0603135d7d7f5cb73b2125beeda19c09f4ef subpackages: - proto + - ptypes/any - name: github.com/golang/snappy - version: d9eb7a3d35ec988b8585d4a0068e462c27d28380 + version: 553a641470496b2327abcac10b36396bd98e45c9 - name: github.com/gorilla/websocket version: 3ab3a8b8831546bd18fd182c20687ca853b2bb13 - name: github.com/hashicorp/hcl @@ -45,9 +46,9 @@ imports: - name: github.com/magiconair/properties version: 51463bfca2576e06c62a8504b5c0f06d61312647 - name: github.com/mattn/go-colorable - version: d228849504861217f796da67fae4f6e347643f15 + version: ded68f7a9561c023e790de24279db7ebf473ea80 - name: github.com/mattn/go-isatty - version: 30a891c33c7cde7b02a981314b4228ec99380cca + version: fc9e8d8ef48496124e79ae0df75490096eccf6fe - name: github.com/mitchellh/mapstructure version: 53818660ed4955e899c0bcafa97299a388bd7c8e - name: github.com/pelletier/go-buffruneio @@ -67,11 +68,11 @@ imports: - name: github.com/spf13/cast version: acbeb36b902d72a7a4c18e8f3241075e7ab763e4 - name: github.com/spf13/cobra - version: fcd0c5a1df88f5d6784cb4feead962c3f3d0b66c + version: 10f6b9d7e1631a54ad07c5c0fb71c28a1abfd3c2 - name: github.com/spf13/jwalterweatherman version: fa7ca7e836cf3a8bb4ebf799f472c12d7e903d66 - name: github.com/spf13/pflag - version: 9ff6c6923cfffbcd502984b8e0c80539a94968b7 + version: 2300d0f8576fe575f71aaa5b9bbe4e1b0dc2eb51 - name: github.com/spf13/viper version: 5d46e70da8c0b6f812e0b170b7a985753b5c63cb - name: github.com/stretchr/testify @@ -80,7 +81,7 @@ imports: - assert - require - name: github.com/syndtr/goleveldb - version: 23851d93a2292dcc56e71a18ec9e0624d84a0f65 + version: 8c81ea47d4c41a385645e133e15510fc6a2a74b4 subpackages: - leveldb - leveldb/cache @@ -95,7 +96,7 @@ imports: - leveldb/table - leveldb/util - name: github.com/tendermint/abci - version: c709d3cc857929a8dd36a90da3640122d7e75770 + version: 8d8e35ae537538c9cf6808be3ca9dd7dab81b7f6 subpackages: - client - example/counter @@ -108,9 +109,9 @@ imports: - edwards25519 - extra25519 - name: github.com/tendermint/go-crypto - version: 9b95da8fa4187f6799558d89b271dc8ab6485615 + version: 197a2b270fd94ee03824b158e738fce62862d0b8 - name: github.com/tendermint/go-wire - version: 334005c236d19c632fb5f073f9de3b0fab6a522b + version: b53add0b622662731985485f3a19be7f684660b8 subpackages: - data - name: github.com/tendermint/log15 @@ -118,7 +119,7 @@ imports: subpackages: - term - name: github.com/tendermint/merkleeyes - version: 6fd69aa0871a4e685a5570aa7ab3d12e4068a722 + version: 0fab643ccac1a3f93b90e0e2682a5d1b9d17f8c4 subpackages: - app - client @@ -137,7 +138,7 @@ imports: - merkle - test - name: golang.org/x/crypto - version: 7c6cc321c680f03b9ef0764448e780704f486b51 + version: 96846453c37f0876340a66a47f3f75b1f3a6cd2d subpackages: - curve25519 - nacl/box @@ -148,7 +149,7 @@ imports: - ripemd160 - salsa20/salsa - name: golang.org/x/net - version: 61557ac0112b576429a0df080e1c2cef5dfbb642 + version: c8c74377599bd978aee1cf3b9b63a8634051cec2 subpackages: - context - http2 @@ -158,25 +159,33 @@ imports: - lex/httplex - trace - name: golang.org/x/sys - version: d75a52659825e75fff6158388dddc6a5b04f9ba5 + version: ea9bcade75cb975a0b9738936568ab388b845617 subpackages: - unix - name: golang.org/x/text - version: f4b4367115ec2de254587813edaa901bc1c723a8 + version: 19e3104b43db45fca0303f489a9536087b184802 subpackages: + - secure/bidirule - transform + - unicode/bidi - unicode/norm +- name: google.golang.org/genproto + version: 411e09b969b1170a9f0c467558eb4c4c110d9c77 + subpackages: + - googleapis/rpc/status - name: google.golang.org/grpc - version: cbcceb2942a489498cf22b2f918536e819d33f0a + version: 6914ab1e338c92da4218a23d27fcd03d0ad78d46 subpackages: - codes - credentials - grpclog - internal + - keepalive - metadata - naming - peer - stats + - status - tap - transport - name: gopkg.in/yaml.v2 From 194f3454700a6532bc2bca00266c48c3706a2e1b Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 28 Apr 2017 14:03:38 +0200 Subject: [PATCH 06/23] Use non-standard port so tests don't die when I am running basecoin --- glide.lock | 2 +- rpc/lib/rpc_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/glide.lock b/glide.lock index 24d44a848..b78270bd7 100644 --- a/glide.lock +++ b/glide.lock @@ -119,7 +119,7 @@ imports: subpackages: - term - name: github.com/tendermint/merkleeyes - version: 0fab643ccac1a3f93b90e0e2682a5d1b9d17f8c4 + version: ea4dd9c7b773435de26bf59fddf90afd43a07d67 subpackages: - app - client diff --git a/rpc/lib/rpc_test.go b/rpc/lib/rpc_test.go index 2ce19fa75..f42fd274c 100644 --- a/rpc/lib/rpc_test.go +++ b/rpc/lib/rpc_test.go @@ -20,10 +20,10 @@ import ( // Client and Server should work over tcp or unix sockets const ( - tcpAddr = "tcp://0.0.0.0:46657" + tcpAddr = "tcp://0.0.0.0:47768" - unixSocket = "/tmp/rpc.sock" - unixAddr = "unix:///tmp/rpc.sock" + unixSocket = "/tmp/rpc_test.sock" + unixAddr = "unix://" + unixSocket websocketEndpoint = "/websocket/endpoint" ) From f6f1f1992cb9d1a54a5d1851bad642efe2e0bd5f Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 28 Apr 2017 14:41:08 +0200 Subject: [PATCH 07/23] Prepare rpc responses for go-data compatibility, still use go-wire --- rpc/client/httpclient.go | 34 +++++----- rpc/core/events.go | 4 +- rpc/core/routes.go | 74 ++++++++++++++-------- rpc/core/types/responses.go | 121 +++++++++++++++++++++++++++--------- rpc/test/client_test.go | 14 ++--- rpc/test/helpers.go | 4 +- 6 files changed, 169 insertions(+), 82 deletions(-) diff --git a/rpc/client/httpclient.go b/rpc/client/httpclient.go index 8f3a4f019..7eecea641 100644 --- a/rpc/client/httpclient.go +++ b/rpc/client/httpclient.go @@ -4,12 +4,12 @@ import ( "fmt" "github.com/pkg/errors" - data "github.com/tendermint/go-wire/data" - events "github.com/tendermint/tmlibs/events" - "github.com/tendermint/tendermint/rpc/lib/client" wire "github.com/tendermint/go-wire" + data "github.com/tendermint/go-wire/data" ctypes "github.com/tendermint/tendermint/rpc/core/types" + "github.com/tendermint/tendermint/rpc/lib/client" "github.com/tendermint/tendermint/types" + events "github.com/tendermint/tmlibs/events" ) /* @@ -56,7 +56,7 @@ func (c *HTTP) Status() (*ctypes.ResultStatus, error) { return nil, errors.Wrap(err, "Status") } // note: panics if rpc doesn't match. okay??? - return (*tmResult).(*ctypes.ResultStatus), nil + return tmResult.Unwrap().(*ctypes.ResultStatus), nil } func (c *HTTP) ABCIInfo() (*ctypes.ResultABCIInfo, error) { @@ -65,7 +65,7 @@ func (c *HTTP) ABCIInfo() (*ctypes.ResultABCIInfo, error) { if err != nil { return nil, errors.Wrap(err, "ABCIInfo") } - return (*tmResult).(*ctypes.ResultABCIInfo), nil + return tmResult.Unwrap().(*ctypes.ResultABCIInfo), nil } func (c *HTTP) ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.ResultABCIQuery, error) { @@ -76,7 +76,7 @@ func (c *HTTP) ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.Resu if err != nil { return nil, errors.Wrap(err, "ABCIQuery") } - return (*tmResult).(*ctypes.ResultABCIQuery), nil + return tmResult.Unwrap().(*ctypes.ResultABCIQuery), nil } func (c *HTTP) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { @@ -85,7 +85,7 @@ func (c *HTTP) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, if err != nil { return nil, errors.Wrap(err, "broadcast_tx_commit") } - return (*tmResult).(*ctypes.ResultBroadcastTxCommit), nil + return tmResult.Unwrap().(*ctypes.ResultBroadcastTxCommit), nil } func (c *HTTP) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { @@ -102,7 +102,7 @@ func (c *HTTP) broadcastTX(route string, tx types.Tx) (*ctypes.ResultBroadcastTx if err != nil { return nil, errors.Wrap(err, route) } - return (*tmResult).(*ctypes.ResultBroadcastTx), nil + return tmResult.Unwrap().(*ctypes.ResultBroadcastTx), nil } func (c *HTTP) NetInfo() (*ctypes.ResultNetInfo, error) { @@ -111,7 +111,7 @@ func (c *HTTP) NetInfo() (*ctypes.ResultNetInfo, error) { if err != nil { return nil, errors.Wrap(err, "NetInfo") } - return (*tmResult).(*ctypes.ResultNetInfo), nil + return tmResult.Unwrap().(*ctypes.ResultNetInfo), nil } func (c *HTTP) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { @@ -120,7 +120,7 @@ func (c *HTTP) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { if err != nil { return nil, errors.Wrap(err, "DumpConsensusState") } - return (*tmResult).(*ctypes.ResultDumpConsensusState), nil + return tmResult.Unwrap().(*ctypes.ResultDumpConsensusState), nil } func (c *HTTP) BlockchainInfo(minHeight, maxHeight int) (*ctypes.ResultBlockchainInfo, error) { @@ -131,7 +131,7 @@ func (c *HTTP) BlockchainInfo(minHeight, maxHeight int) (*ctypes.ResultBlockchai if err != nil { return nil, errors.Wrap(err, "BlockchainInfo") } - return (*tmResult).(*ctypes.ResultBlockchainInfo), nil + return tmResult.Unwrap().(*ctypes.ResultBlockchainInfo), nil } func (c *HTTP) Genesis() (*ctypes.ResultGenesis, error) { @@ -140,7 +140,7 @@ func (c *HTTP) Genesis() (*ctypes.ResultGenesis, error) { if err != nil { return nil, errors.Wrap(err, "Genesis") } - return (*tmResult).(*ctypes.ResultGenesis), nil + return tmResult.Unwrap().(*ctypes.ResultGenesis), nil } func (c *HTTP) Block(height int) (*ctypes.ResultBlock, error) { @@ -149,7 +149,7 @@ func (c *HTTP) Block(height int) (*ctypes.ResultBlock, error) { if err != nil { return nil, errors.Wrap(err, "Block") } - return (*tmResult).(*ctypes.ResultBlock), nil + return tmResult.Unwrap().(*ctypes.ResultBlock), nil } func (c *HTTP) Commit(height int) (*ctypes.ResultCommit, error) { @@ -158,7 +158,7 @@ func (c *HTTP) Commit(height int) (*ctypes.ResultCommit, error) { if err != nil { return nil, errors.Wrap(err, "Commit") } - return (*tmResult).(*ctypes.ResultCommit), nil + return tmResult.Unwrap().(*ctypes.ResultCommit), nil } func (c *HTTP) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) { @@ -171,7 +171,7 @@ func (c *HTTP) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) { if err != nil { return nil, errors.Wrap(err, "Tx") } - return (*tmResult).(*ctypes.ResultTx), nil + return tmResult.Unwrap().(*ctypes.ResultTx), nil } func (c *HTTP) Validators() (*ctypes.ResultValidators, error) { @@ -180,7 +180,7 @@ func (c *HTTP) Validators() (*ctypes.ResultValidators, error) { if err != nil { return nil, errors.Wrap(err, "Validators") } - return (*tmResult).(*ctypes.ResultValidators), nil + return tmResult.Unwrap().(*ctypes.ResultValidators), nil } /** websocket event stuff here... **/ @@ -340,7 +340,7 @@ func (w *WSEvents) parseEvent(data []byte) (err error) { if err != nil { return err } - event, ok := (*result).(*ctypes.ResultEvent) + event, ok := result.Unwrap().(*ctypes.ResultEvent) if !ok { // ignore silently (eg. subscribe, unsubscribe and maybe other events) return nil diff --git a/rpc/core/events.go b/rpc/core/events.go index 555673df5..271ba5301 100644 --- a/rpc/core/events.go +++ b/rpc/core/events.go @@ -1,8 +1,8 @@ package core import ( - "github.com/tendermint/tendermint/rpc/lib/types" ctypes "github.com/tendermint/tendermint/rpc/core/types" + "github.com/tendermint/tendermint/rpc/lib/types" "github.com/tendermint/tendermint/types" ) @@ -11,7 +11,7 @@ func Subscribe(wsCtx rpctypes.WSRPCContext, event string) (*ctypes.ResultSubscri types.AddListenerForEvent(wsCtx.GetEventSwitch(), wsCtx.GetRemoteAddr(), event, func(msg types.TMEventData) { // NOTE: EventSwitch callbacks must be nonblocking // NOTE: RPCResponses of subscribed events have id suffix "#event" - tmResult := ctypes.TMResult(&ctypes.ResultEvent{event, msg}) + tmResult := ctypes.TMResult{&ctypes.ResultEvent{event, msg}} wsCtx.TryWriteRPCResponse(rpctypes.NewRPCResponse(wsCtx.Request.ID+"#event", &tmResult, "")) }) return &ctypes.ResultSubscribe{}, nil diff --git a/rpc/core/routes.go b/rpc/core/routes.go index 12694ab76..9e4c03be1 100644 --- a/rpc/core/routes.go +++ b/rpc/core/routes.go @@ -2,8 +2,8 @@ package core import ( data "github.com/tendermint/go-wire/data" - rpc "github.com/tendermint/tendermint/rpc/lib/server" ctypes "github.com/tendermint/tendermint/rpc/core/types" + rpc "github.com/tendermint/tendermint/rpc/lib/server" "github.com/tendermint/tendermint/rpc/lib/types" "github.com/tendermint/tendermint/types" ) @@ -50,100 +50,124 @@ var Routes = map[string]*rpc.RPCFunc{ } func SubscribeResult(wsCtx rpctypes.WSRPCContext, event string) (ctypes.TMResult, error) { - return Subscribe(wsCtx, event) + res, err := Subscribe(wsCtx, event) + return ctypes.TMResult{res}, err } func UnsubscribeResult(wsCtx rpctypes.WSRPCContext, event string) (ctypes.TMResult, error) { - return Unsubscribe(wsCtx, event) + res, err := Unsubscribe(wsCtx, event) + return ctypes.TMResult{res}, err } func StatusResult() (ctypes.TMResult, error) { - return Status() + res, err := Status() + return ctypes.TMResult{res}, err } func NetInfoResult() (ctypes.TMResult, error) { - return NetInfo() + res, err := NetInfo() + return ctypes.TMResult{res}, err } func UnsafeDialSeedsResult(seeds []string) (ctypes.TMResult, error) { - return UnsafeDialSeeds(seeds) + res, err := UnsafeDialSeeds(seeds) + return ctypes.TMResult{res}, err } func BlockchainInfoResult(min, max int) (ctypes.TMResult, error) { - return BlockchainInfo(min, max) + res, err := BlockchainInfo(min, max) + return ctypes.TMResult{res}, err } func GenesisResult() (ctypes.TMResult, error) { - return Genesis() + res, err := Genesis() + return ctypes.TMResult{res}, err } func BlockResult(height int) (ctypes.TMResult, error) { - return Block(height) + res, err := Block(height) + return ctypes.TMResult{res}, err } func CommitResult(height int) (ctypes.TMResult, error) { - return Commit(height) + res, err := Commit(height) + return ctypes.TMResult{res}, err } func ValidatorsResult() (ctypes.TMResult, error) { - return Validators() + res, err := Validators() + return ctypes.TMResult{res}, err } func DumpConsensusStateResult() (ctypes.TMResult, error) { - return DumpConsensusState() + res, err := DumpConsensusState() + return ctypes.TMResult{res}, err } func UnconfirmedTxsResult() (ctypes.TMResult, error) { - return UnconfirmedTxs() + res, err := UnconfirmedTxs() + return ctypes.TMResult{res}, err } func NumUnconfirmedTxsResult() (ctypes.TMResult, error) { - return NumUnconfirmedTxs() + res, err := NumUnconfirmedTxs() + return ctypes.TMResult{res}, err } // 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, prove bool) (ctypes.TMResult, error) { - return Tx(hash, prove) + res, err := Tx(hash, prove) + return ctypes.TMResult{res}, err } func BroadcastTxCommitResult(tx types.Tx) (ctypes.TMResult, error) { - return BroadcastTxCommit(tx) + res, err := BroadcastTxCommit(tx) + return ctypes.TMResult{res}, err } func BroadcastTxSyncResult(tx types.Tx) (ctypes.TMResult, error) { - return BroadcastTxSync(tx) + res, err := BroadcastTxSync(tx) + return ctypes.TMResult{res}, err } func BroadcastTxAsyncResult(tx types.Tx) (ctypes.TMResult, error) { - return BroadcastTxAsync(tx) + res, err := BroadcastTxAsync(tx) + return ctypes.TMResult{res}, err } func ABCIQueryResult(path string, data data.Bytes, prove bool) (ctypes.TMResult, error) { - return ABCIQuery(path, data, prove) + res, err := ABCIQuery(path, data, prove) + return ctypes.TMResult{res}, err } func ABCIInfoResult() (ctypes.TMResult, error) { - return ABCIInfo() + res, err := ABCIInfo() + return ctypes.TMResult{res}, err } func UnsafeFlushMempoolResult() (ctypes.TMResult, error) { - return UnsafeFlushMempool() + res, err := UnsafeFlushMempool() + return ctypes.TMResult{res}, err } func UnsafeSetConfigResult(typ, key, value string) (ctypes.TMResult, error) { - return UnsafeSetConfig(typ, key, value) + res, err := UnsafeSetConfig(typ, key, value) + return ctypes.TMResult{res}, err } func UnsafeStartCPUProfilerResult(filename string) (ctypes.TMResult, error) { - return UnsafeStartCPUProfiler(filename) + res, err := UnsafeStartCPUProfiler(filename) + return ctypes.TMResult{res}, err } func UnsafeStopCPUProfilerResult() (ctypes.TMResult, error) { - return UnsafeStopCPUProfiler() + res, err := UnsafeStopCPUProfiler() + return ctypes.TMResult{res}, err } func UnsafeWriteHeapProfileResult(filename string) (ctypes.TMResult, error) { - return UnsafeWriteHeapProfile(filename) + res, err := UnsafeWriteHeapProfile(filename) + return ctypes.TMResult{res}, err } diff --git a/rpc/core/types/responses.go b/rpc/core/types/responses.go index ae2340a78..98056a8ce 100644 --- a/rpc/core/types/responses.go +++ b/rpc/core/types/responses.go @@ -5,7 +5,6 @@ import ( abci "github.com/tendermint/abci/types" "github.com/tendermint/go-crypto" - "github.com/tendermint/go-wire" "github.com/tendermint/go-wire/data" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/rpc/lib/types" @@ -175,34 +174,98 @@ const ( ResultTypeUnsafeFlushMempool = byte(0xa4) ) -type TMResult interface { +const ( + // for the blockchain + ResultNameGenesis = "genesis" + ResultNameBlockchainInfo = "info" + ResultNameBlock = "block" + ResultNameCommit = "commit" + + // for the network + ResultNameStatus = "status" + ResultNameNetInfo = "netinfo" + ResultNameDialSeeds = "dialseeds" + + // for the consensus + ResultNameValidators = "validators" + ResultNameDumpConsensusState = "consensus" + + // for txs / the application + ResultNameBroadcastTx = "broadcast_tx" + ResultNameUnconfirmedTxs = "unconfirmed_tx" + ResultNameBroadcastTxCommit = "broadcast_tx_commit" + ResultNameTx = "tx" + + // for querying the application + ResultNameABCIQuery = "abci_query" + ResultNameABCIInfo = "abci_info" + + // for events + ResultNameSubscribe = "subscribe" + ResultNameUnsubscribe = "unsubscribe" + ResultNameEvent = "event" + + // for testing + ResultNameUnsafeSetConfig = "unsafe_set_config" + ResultNameUnsafeStartCPUProfiler = "unsafe_start_profiler" + ResultNameUnsafeStopCPUProfiler = "unsafe_stop_profiler" + ResultNameUnsafeWriteHeapProfile = "unsafe_write_heap" + ResultNameUnsafeFlushMempool = "unsafe_flush_mempool" +) + +type TMResultInner interface { rpctypes.Result } -// for wire.readReflect -var _ = wire.RegisterInterface( - struct{ TMResult }{}, - wire.ConcreteType{&ResultGenesis{}, ResultTypeGenesis}, - wire.ConcreteType{&ResultBlockchainInfo{}, ResultTypeBlockchainInfo}, - wire.ConcreteType{&ResultBlock{}, ResultTypeBlock}, - wire.ConcreteType{&ResultCommit{}, ResultTypeCommit}, - wire.ConcreteType{&ResultStatus{}, ResultTypeStatus}, - wire.ConcreteType{&ResultNetInfo{}, ResultTypeNetInfo}, - wire.ConcreteType{&ResultDialSeeds{}, ResultTypeDialSeeds}, - wire.ConcreteType{&ResultValidators{}, ResultTypeValidators}, - wire.ConcreteType{&ResultDumpConsensusState{}, ResultTypeDumpConsensusState}, - wire.ConcreteType{&ResultBroadcastTx{}, ResultTypeBroadcastTx}, - wire.ConcreteType{&ResultBroadcastTxCommit{}, ResultTypeBroadcastTxCommit}, - wire.ConcreteType{&ResultTx{}, ResultTypeTx}, - wire.ConcreteType{&ResultUnconfirmedTxs{}, ResultTypeUnconfirmedTxs}, - wire.ConcreteType{&ResultSubscribe{}, ResultTypeSubscribe}, - wire.ConcreteType{&ResultUnsubscribe{}, ResultTypeUnsubscribe}, - wire.ConcreteType{&ResultEvent{}, ResultTypeEvent}, - wire.ConcreteType{&ResultUnsafeSetConfig{}, ResultTypeUnsafeSetConfig}, - wire.ConcreteType{&ResultUnsafeProfile{}, ResultTypeUnsafeStartCPUProfiler}, - wire.ConcreteType{&ResultUnsafeProfile{}, ResultTypeUnsafeStopCPUProfiler}, - wire.ConcreteType{&ResultUnsafeProfile{}, ResultTypeUnsafeWriteHeapProfile}, - wire.ConcreteType{&ResultUnsafeFlushMempool{}, ResultTypeUnsafeFlushMempool}, - wire.ConcreteType{&ResultABCIQuery{}, ResultTypeABCIQuery}, - wire.ConcreteType{&ResultABCIInfo{}, ResultTypeABCIInfo}, -) +type TMResult struct { + TMResultInner +} + +func (tmr TMResult) MarshalJSON() ([]byte, error) { + return tmResultMapper.ToJSON(tmr.TMResultInner) +} + +func (tmr *TMResult) UnmarshalJSON(data []byte) (err error) { + parsed, err := tmResultMapper.FromJSON(data) + if err == nil && parsed != nil { + tmr.TMResultInner = parsed.(TMResultInner) + } + return +} + +func (tmr TMResult) Unwrap() TMResultInner { + tmrI := tmr.TMResultInner + for wrap, ok := tmrI.(TMResult); ok; wrap, ok = tmrI.(TMResult) { + tmrI = wrap.TMResultInner + } + return tmrI +} + +func (tmr TMResult) Empty() bool { + return tmr.TMResultInner == nil +} + +var tmResultMapper = data.NewMapper(TMResult{}). + RegisterImplementation(&ResultGenesis{}, ResultNameGenesis, ResultTypeGenesis). + RegisterImplementation(&ResultBlockchainInfo{}, ResultNameBlockchainInfo, ResultTypeBlockchainInfo). + RegisterImplementation(&ResultBlock{}, ResultNameBlock, ResultTypeBlock). + RegisterImplementation(&ResultCommit{}, ResultNameCommit, ResultTypeCommit). + RegisterImplementation(&ResultStatus{}, ResultNameStatus, ResultTypeStatus). + RegisterImplementation(&ResultNetInfo{}, ResultNameNetInfo, ResultTypeNetInfo). + RegisterImplementation(&ResultDialSeeds{}, ResultNameDialSeeds, ResultTypeDialSeeds). + RegisterImplementation(&ResultValidators{}, ResultNameValidators, ResultTypeValidators). + RegisterImplementation(&ResultDumpConsensusState{}, ResultNameDumpConsensusState, ResultTypeDumpConsensusState). + RegisterImplementation(&ResultBroadcastTx{}, ResultNameBroadcastTx, ResultTypeBroadcastTx). + RegisterImplementation(&ResultBroadcastTxCommit{}, ResultNameBroadcastTxCommit, ResultTypeBroadcastTxCommit). + RegisterImplementation(&ResultTx{}, ResultNameTx, ResultTypeTx). + RegisterImplementation(&ResultUnconfirmedTxs{}, ResultNameUnconfirmedTxs, ResultTypeUnconfirmedTxs). + RegisterImplementation(&ResultSubscribe{}, ResultNameSubscribe, ResultTypeSubscribe). + RegisterImplementation(&ResultUnsubscribe{}, ResultNameUnsubscribe, ResultTypeUnsubscribe). + RegisterImplementation(&ResultEvent{}, ResultNameEvent, ResultTypeEvent). + RegisterImplementation(&ResultUnsafeSetConfig{}, ResultNameUnsafeSetConfig, ResultTypeUnsafeSetConfig). + RegisterImplementation(&ResultUnsafeProfile{}, ResultNameUnsafeStartCPUProfiler, ResultTypeUnsafeStartCPUProfiler). + RegisterImplementation(&ResultUnsafeProfile{}, ResultNameUnsafeStopCPUProfiler, ResultTypeUnsafeStopCPUProfiler). + RegisterImplementation(&ResultUnsafeProfile{}, ResultNameUnsafeWriteHeapProfile, ResultTypeUnsafeWriteHeapProfile). + RegisterImplementation(&ResultUnsafeFlushMempool{}, ResultNameUnsafeFlushMempool, ResultTypeUnsafeFlushMempool). + RegisterImplementation(&ResultABCIQuery{}, ResultNameABCIQuery, ResultTypeABCIQuery). + RegisterImplementation(&ResultABCIInfo{}, ResultNameABCIInfo, ResultTypeABCIInfo) diff --git a/rpc/test/client_test.go b/rpc/test/client_test.go index 6d0905fde..c9a922d72 100644 --- a/rpc/test/client_test.go +++ b/rpc/test/client_test.go @@ -42,7 +42,7 @@ func testStatus(t *testing.T, client rpc.HTTPClient) { _, err := client.Call("status", map[string]interface{}{}, tmResult) require.Nil(t, err) - status := (*tmResult).(*ctypes.ResultStatus) + status := tmResult.Unwrap().(*ctypes.ResultStatus) assert.Equal(t, chainID, status.NodeInfo.Network) } @@ -74,7 +74,7 @@ func testBroadcastTxSync(t *testing.T, client rpc.HTTPClient) { _, err := client.Call("broadcast_tx_sync", map[string]interface{}{"tx": tx}, tmResult) require.Nil(t, err) - res := (*tmResult).(*ctypes.ResultBroadcastTx) + res := tmResult.Unwrap().(*ctypes.ResultBroadcastTx) require.Equal(t, abci.CodeType_OK, res.Code) require.Equal(t, initMemSize+1, mem.Size()) txs := mem.Reap(1) @@ -96,7 +96,7 @@ func sendTx(t *testing.T, client rpc.HTTPClient) ([]byte, []byte) { k, v, tx := testTxKV(t) _, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, tmResult) require.Nil(t, err) - bres := (*tmResult).(*ctypes.ResultBroadcastTxCommit) + bres := tmResult.Unwrap().(*ctypes.ResultBroadcastTxCommit) require.NotNil(t, 0, bres.DeliverTx, "%#v", bres) require.EqualValues(t, 0, bres.CheckTx.Code, "%#v", bres) require.EqualValues(t, 0, bres.DeliverTx.Code, "%#v", bres) @@ -119,7 +119,7 @@ func testABCIQuery(t *testing.T, client rpc.HTTPClient) { map[string]interface{}{"path": "", "data": k, "prove": false}, tmResult) require.Nil(t, err) - resQuery := (*tmResult).(*ctypes.ResultABCIQuery) + resQuery := tmResult.Unwrap().(*ctypes.ResultABCIQuery) require.EqualValues(t, 0, resQuery.Code) // XXX: specific to value returned by the dummy @@ -145,7 +145,7 @@ func testBroadcastTxCommit(t *testing.T, client rpc.HTTPClient) { _, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, tmResult) require.Nil(err) - res := (*tmResult).(*ctypes.ResultBroadcastTxCommit) + res := tmResult.Unwrap().(*ctypes.ResultBroadcastTxCommit) checkTx := res.CheckTx require.Equal(abci.CodeType_OK, checkTx.Code) deliverTx := res.DeliverTx @@ -184,7 +184,7 @@ func testTx(t *testing.T, client rpc.HTTPClient, withIndexer bool) { _, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": txBytes}, tmResult) require.Nil(err) - res := (*tmResult).(*ctypes.ResultBroadcastTxCommit) + res := tmResult.Unwrap().(*ctypes.ResultBroadcastTxCommit) checkTx := res.CheckTx require.Equal(abci.CodeType_OK, checkTx.Code) deliverTx := res.DeliverTx @@ -225,7 +225,7 @@ func testTx(t *testing.T, client rpc.HTTPClient, withIndexer bool) { require.NotNil(err, idx) } else { require.Nil(err, idx) - res2 := (*tmResult).(*ctypes.ResultTx) + res2 := tmResult.Unwrap().(*ctypes.ResultTx) assert.Equal(tx, res2.Tx, idx) assert.Equal(res.Height, res2.Height, idx) assert.Equal(0, res2.Index, idx) diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index 311f05980..622890596 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -18,9 +18,9 @@ import ( "github.com/tendermint/tendermint/config/tendermint_test" nm "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/proxy" - client "github.com/tendermint/tendermint/rpc/lib/client" ctypes "github.com/tendermint/tendermint/rpc/core/types" core_grpc "github.com/tendermint/tendermint/rpc/grpc" + client "github.com/tendermint/tendermint/rpc/lib/client" "github.com/tendermint/tendermint/types" ) @@ -137,7 +137,7 @@ func waitForEvent(t *testing.T, wsc *client.WSClient, eventid string, dieOnTimeo errCh <- err break LOOP } - event, ok := (*result).(*ctypes.ResultEvent) + event, ok := result.Unwrap().(*ctypes.ResultEvent) if ok && event.Name == eventid { goodCh <- event.Data break LOOP From bff8402fe866c5234e212e3de8019375c2615ff6 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 28 Apr 2017 15:26:06 +0200 Subject: [PATCH 08/23] Fix json for TMResult to not include "TMResultInner" --- rpc/core/types/responses.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc/core/types/responses.go b/rpc/core/types/responses.go index 98056a8ce..9b4e9d579 100644 --- a/rpc/core/types/responses.go +++ b/rpc/core/types/responses.go @@ -218,7 +218,7 @@ type TMResultInner interface { } type TMResult struct { - TMResultInner + TMResultInner `json:"unwrap"` } func (tmr TMResult) MarshalJSON() ([]byte, error) { From 6c60c07f16f09e0f22ab2c3825b258c6b7690b4b Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 28 Apr 2017 16:24:06 +0200 Subject: [PATCH 09/23] BROKEN: attempt to replace go-wire.JSON with json.Unmarshall in rpc --- rpc/client/httpclient.go | 4 +-- rpc/lib/client/http_client.go | 22 +++++------- rpc/lib/client/ws_client.go | 15 ++++---- rpc/lib/rpc_test.go | 67 ++++++++++++++++++++++++----------- rpc/lib/server/handlers.go | 6 ++-- rpc/lib/types/types.go | 11 ++++-- rpc/test/helpers.go | 4 +-- 7 files changed, 77 insertions(+), 52 deletions(-) diff --git a/rpc/client/httpclient.go b/rpc/client/httpclient.go index 7eecea641..662ced895 100644 --- a/rpc/client/httpclient.go +++ b/rpc/client/httpclient.go @@ -1,10 +1,10 @@ package client import ( + "encoding/json" "fmt" "github.com/pkg/errors" - wire "github.com/tendermint/go-wire" data "github.com/tendermint/go-wire/data" ctypes "github.com/tendermint/tendermint/rpc/core/types" "github.com/tendermint/tendermint/rpc/lib/client" @@ -336,7 +336,7 @@ func (w *WSEvents) eventListener() { // on the merry way to the EventSwitch func (w *WSEvents) parseEvent(data []byte) (err error) { result := new(ctypes.TMResult) - wire.ReadJSONPtr(result, data, &err) + err = json.Unmarshal(data, result) if err != nil { return err } diff --git a/rpc/lib/client/http_client.go b/rpc/lib/client/http_client.go index 45ff8b8ac..55963f506 100644 --- a/rpc/lib/client/http_client.go +++ b/rpc/lib/client/http_client.go @@ -12,7 +12,6 @@ import ( "strings" "github.com/pkg/errors" - wire "github.com/tendermint/go-wire" types "github.com/tendermint/tendermint/rpc/lib/types" ) @@ -70,15 +69,15 @@ func NewJSONRPCClient(remote string) *JSONRPCClient { func (c *JSONRPCClient) Call(method string, params map[string]interface{}, result interface{}) (interface{}, error) { // we need this step because we attempt to decode values using `go-wire` // (handlers.go:176) on the server side - encodedParams := make(map[string]interface{}) - for k, v := range params { - bytes := json.RawMessage(wire.JSONBytes(v)) - encodedParams[k] = &bytes - } + // encodedParams := make(map[string]interface{}) + // for k, v := range params { + // bytes := json.RawMessage(wire.JSONBytes(v)) + // encodedParams[k] = &bytes + // } request := types.RPCRequest{ JSONRPC: "2.0", Method: method, - Params: encodedParams, + Params: params, ID: "", } requestBytes, err := json.Marshal(request) @@ -153,7 +152,7 @@ func unmarshalResponseBytes(responseBytes []byte, result interface{}) (interface return nil, errors.Errorf("Response error: %v", errorStr) } // unmarshal the RawMessage into the result - result = wire.ReadJSONPtr(result, *response.Result, &err) + err = json.Unmarshal(*response.Result, result) if err != nil { return nil, errors.Errorf("Error unmarshalling rpc response result: %v", err) } @@ -176,8 +175,6 @@ func argsToURLValues(args map[string]interface{}) (url.Values, error) { } func argsToJson(args map[string]interface{}) error { - var n int - var err error for k, v := range args { rt := reflect.TypeOf(v) isByteSlice := rt.Kind() == reflect.Slice && rt.Elem().Kind() == reflect.Uint8 @@ -188,12 +185,11 @@ func argsToJson(args map[string]interface{}) error { } // Pass everything else to go-wire - buf := new(bytes.Buffer) - wire.WriteJSON(v, buf, &n, &err) + data, err := json.Marshal(v) if err != nil { return err } - args[k] = buf.String() + args[k] = string(data) } return nil } diff --git a/rpc/lib/client/ws_client.go b/rpc/lib/client/ws_client.go index ad922dd68..8884068b9 100644 --- a/rpc/lib/client/ws_client.go +++ b/rpc/lib/client/ws_client.go @@ -8,9 +8,8 @@ import ( "github.com/gorilla/websocket" "github.com/pkg/errors" - cmn "github.com/tendermint/tmlibs/common" types "github.com/tendermint/tendermint/rpc/lib/types" - wire "github.com/tendermint/go-wire" + cmn "github.com/tendermint/tmlibs/common" ) const ( @@ -157,15 +156,15 @@ func (wsc *WSClient) Unsubscribe(eventid string) error { func (wsc *WSClient) Call(method string, params map[string]interface{}) error { // we need this step because we attempt to decode values using `go-wire` // (handlers.go:470) on the server side - encodedParams := make(map[string]interface{}) - for k, v := range params { - bytes := json.RawMessage(wire.JSONBytes(v)) - encodedParams[k] = &bytes - } + // encodedParams := make(map[string]interface{}) + // for k, v := range params { + // bytes := json.RawMessage(wire.JSONBytes(v)) + // encodedParams[k] = &bytes + // } err := wsc.WriteJSON(types.RPCRequest{ JSONRPC: "2.0", Method: method, - Params: encodedParams, + Params: params, ID: "", }) return err diff --git a/rpc/lib/rpc_test.go b/rpc/lib/rpc_test.go index f42fd274c..cd42f20a6 100644 --- a/rpc/lib/rpc_test.go +++ b/rpc/lib/rpc_test.go @@ -3,6 +3,7 @@ package rpc import ( "bytes" crand "crypto/rand" + "encoding/json" "fmt" "math/rand" "net/http" @@ -12,7 +13,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - wire "github.com/tendermint/go-wire" + data "github.com/tendermint/go-data" client "github.com/tendermint/tendermint/rpc/lib/client" server "github.com/tendermint/tendermint/rpc/lib/server" types "github.com/tendermint/tendermint/rpc/lib/types" @@ -29,7 +30,35 @@ const ( ) // Define a type for results and register concrete versions -type Result interface{} +type ResultInner interface{} + +type Result struct { + ResultInner `json:"unwrap"` +} + +func (r Result) MarshalJSON() ([]byte, error) { + return resultMapper.ToJSON(r.ResultInner) +} + +func (r *Result) UnmarshalJSON(data []byte) (err error) { + parsed, err := resultMapper.FromJSON(data) + if err == nil && parsed != nil { + r.ResultInner = parsed.(ResultInner) + } + return +} + +func (r Result) Unwrap() ResultInner { + tmrI := r.ResultInner + for wrap, ok := tmrI.(Result); ok; wrap, ok = tmrI.(Result) { + tmrI = wrap.ResultInner + } + return tmrI +} + +func (r Result) Empty() bool { + return r.ResultInner == nil +} type ResultEcho struct { Value string @@ -39,11 +68,9 @@ type ResultEchoBytes struct { Value []byte } -var _ = wire.RegisterInterface( - struct{ Result }{}, - wire.ConcreteType{&ResultEcho{}, 0x1}, - wire.ConcreteType{&ResultEchoBytes{}, 0x2}, -) +var resultMapper = data.NewMapper(Result{}). + RegisterImplementation(&ResultEcho{}, "echo", 0x1). + RegisterImplementation(&ResultEchoBytes{}, "echo_bytes", 0x2) // Define some routes var Routes = map[string]*server.RPCFunc{ @@ -53,15 +80,15 @@ var Routes = map[string]*server.RPCFunc{ } func EchoResult(v string) (Result, error) { - return &ResultEcho{v}, nil + return Result{&ResultEcho{v}}, nil } func EchoWSResult(wsCtx types.WSRPCContext, v string) (Result, error) { - return &ResultEcho{v}, nil + return Result{&ResultEcho{v}}, nil } func EchoBytesResult(v []byte) (Result, error) { - return &ResultEchoBytes{v}, nil + return Result{&ResultEchoBytes{v}}, nil } // launch unix and tcp servers @@ -109,7 +136,7 @@ func echoViaHTTP(cl client.HTTPClient, val string) (string, error) { if _, err := cl.Call("echo", params, &result); err != nil { return "", err } - return result.(*ResultEcho).Value, nil + return result.Unwrap().(*ResultEcho).Value, nil } func echoBytesViaHTTP(cl client.HTTPClient, bytes []byte) ([]byte, error) { @@ -120,7 +147,7 @@ func echoBytesViaHTTP(cl client.HTTPClient, bytes []byte) ([]byte, error) { if _, err := cl.Call("echo_bytes", params, &result); err != nil { return []byte{}, err } - return result.(*ResultEchoBytes).Value, nil + return result.Unwrap().(*ResultEchoBytes).Value, nil } func testWithHTTPClient(t *testing.T, cl client.HTTPClient) { @@ -147,11 +174,11 @@ func echoViaWS(cl *client.WSClient, val string) (string, error) { select { case msg := <-cl.ResultsCh: result := new(Result) - wire.ReadJSONPtr(result, msg, &err) + err = json.Unmarshal(msg, result) if err != nil { return "", nil } - return (*result).(*ResultEcho).Value, nil + return result.Unwrap().(*ResultEcho).Value, nil case err := <-cl.ErrorsCh: return "", err } @@ -169,11 +196,11 @@ func echoBytesViaWS(cl *client.WSClient, bytes []byte) ([]byte, error) { select { case msg := <-cl.ResultsCh: result := new(Result) - wire.ReadJSONPtr(result, msg, &err) + err = json.Unmarshal(msg, result) if err != nil { return []byte{}, nil } - return (*result).(*ResultEchoBytes).Value, nil + return result.Unwrap().(*ResultEchoBytes).Value, nil case err := <-cl.ErrorsCh: return []byte{}, err } @@ -252,9 +279,9 @@ func TestWSNewWSRPCFunc(t *testing.T) { select { case msg := <-cl.ResultsCh: result := new(Result) - wire.ReadJSONPtr(result, msg, &err) + err = json.Unmarshal(msg, result) require.Nil(t, err) - got := (*result).(*ResultEcho).Value + got := result.Unwrap().(*ResultEcho).Value assert.Equal(t, got, val) case err := <-cl.ErrorsCh: t.Fatal(err) @@ -280,9 +307,9 @@ func TestWSHandlesArrayParams(t *testing.T) { select { case msg := <-cl.ResultsCh: result := new(Result) - wire.ReadJSONPtr(result, msg, &err) + err = json.Unmarshal(msg, result) require.Nil(t, err) - got := (*result).(*ResultEcho).Value + got := result.Unwrap().(*ResultEcho).Value assert.Equal(t, got, val) case err := <-cl.ErrorsCh: t.Fatalf("%+v", err) diff --git a/rpc/lib/server/handlers.go b/rpc/lib/server/handlers.go index d7dffd402..27451c23d 100644 --- a/rpc/lib/server/handlers.go +++ b/rpc/lib/server/handlers.go @@ -280,9 +280,8 @@ func httpParamsToArgs(rpcFunc *RPCFunc, r *http.Request) ([]reflect.Value, error } func _jsonStringToArg(ty reflect.Type, arg string) (reflect.Value, error) { - var err error v := reflect.New(ty) - wire.ReadJSONPtr(v.Interface(), []byte(arg), &err) + err := json.Unmarshal([]byte(arg), v.Interface()) if err != nil { return v, err } @@ -315,9 +314,8 @@ func nonJsonToArg(ty reflect.Type, arg string) (reflect.Value, error, bool) { } if isQuotedString && expectingByteSlice { - var err error v := reflect.New(reflect.TypeOf("")) - wire.ReadJSONPtr(v.Interface(), []byte(arg), &err) + err := json.Unmarshal([]byte(arg), v.Interface()) if err != nil { return reflect.ValueOf(nil), err, false } diff --git a/rpc/lib/types/types.go b/rpc/lib/types/types.go index 9c5f2625b..28ddfc0ff 100644 --- a/rpc/lib/types/types.go +++ b/rpc/lib/types/types.go @@ -4,7 +4,6 @@ import ( "encoding/json" "strings" - wire "github.com/tendermint/go-wire" events "github.com/tendermint/tmlibs/events" ) @@ -52,8 +51,14 @@ type RPCResponse struct { func NewRPCResponse(id string, res interface{}, err string) RPCResponse { var raw *json.RawMessage if res != nil { - rawMsg := json.RawMessage(wire.JSONBytes(res)) - raw = &rawMsg + var js []byte + js, err2 := json.Marshal(res) + if err2 == nil { + rawMsg := json.RawMessage(js) + raw = &rawMsg + } else { + err = err2.Error() + } } return RPCResponse{ JSONRPC: "2.0", diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index 622890596..76d04f511 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -1,6 +1,7 @@ package rpctest import ( + "encoding/json" "fmt" "math/rand" "os" @@ -11,7 +12,6 @@ import ( "github.com/spf13/viper" "github.com/stretchr/testify/require" - wire "github.com/tendermint/go-wire" logger "github.com/tendermint/tmlibs/logger" abci "github.com/tendermint/abci/types" @@ -132,7 +132,7 @@ func waitForEvent(t *testing.T, wsc *client.WSClient, eventid string, dieOnTimeo select { case r := <-wsc.ResultsCh: result := new(ctypes.TMResult) - wire.ReadJSONPtr(result, r, &err) + err = json.Unmarshal(r, result) if err != nil { errCh <- err break LOOP From acfbea6d4965fd3e7063d54fedc3a2d48404f066 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 28 Apr 2017 14:36:38 -0400 Subject: [PATCH 10/23] rpc: decode args without wire --- rpc/lib/rpc_test.go | 64 +++++++++++++++++++++++++++++++++++--- rpc/lib/server/handlers.go | 31 ++++++++++++++++-- 2 files changed, 88 insertions(+), 7 deletions(-) diff --git a/rpc/lib/rpc_test.go b/rpc/lib/rpc_test.go index cd42f20a6..2f61f3887 100644 --- a/rpc/lib/rpc_test.go +++ b/rpc/lib/rpc_test.go @@ -13,7 +13,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - data "github.com/tendermint/go-data" + "github.com/tendermint/go-wire/data" client "github.com/tendermint/tendermint/rpc/lib/client" server "github.com/tendermint/tendermint/rpc/lib/server" types "github.com/tendermint/tendermint/rpc/lib/types" @@ -64,19 +64,31 @@ type ResultEcho struct { Value string } +type ResultEchoInt struct { + Value int +} + type ResultEchoBytes struct { Value []byte } +type ResultEchoDataBytes struct { + Value data.Bytes +} + var resultMapper = data.NewMapper(Result{}). RegisterImplementation(&ResultEcho{}, "echo", 0x1). - RegisterImplementation(&ResultEchoBytes{}, "echo_bytes", 0x2) + RegisterImplementation(&ResultEchoBytes{}, "echo_bytes", 0x2). + RegisterImplementation(&ResultEchoDataBytes{}, "echo_data_bytes", 0x3). + RegisterImplementation(&ResultEchoInt{}, "echo_int", 0x4) // Define some routes var Routes = map[string]*server.RPCFunc{ - "echo": server.NewRPCFunc(EchoResult, "arg"), - "echo_ws": server.NewWSRPCFunc(EchoWSResult, "arg"), - "echo_bytes": server.NewRPCFunc(EchoBytesResult, "arg"), + "echo": server.NewRPCFunc(EchoResult, "arg"), + "echo_ws": server.NewWSRPCFunc(EchoWSResult, "arg"), + "echo_bytes": server.NewRPCFunc(EchoBytesResult, "arg"), + "echo_data_bytes": server.NewRPCFunc(EchoDataBytesResult, "arg"), + "echo_int": server.NewRPCFunc(EchoIntResult, "arg"), } func EchoResult(v string) (Result, error) { @@ -87,10 +99,18 @@ func EchoWSResult(wsCtx types.WSRPCContext, v string) (Result, error) { return Result{&ResultEcho{v}}, nil } +func EchoIntResult(v int) (Result, error) { + return Result{&ResultEchoInt{v}}, nil +} + func EchoBytesResult(v []byte) (Result, error) { return Result{&ResultEchoBytes{v}}, nil } +func EchoDataBytesResult(v data.Bytes) (Result, error) { + return Result{&ResultEchoDataBytes{v}}, nil +} + // launch unix and tcp servers func init() { cmd := exec.Command("rm", "-f", unixSocket) @@ -139,6 +159,17 @@ func echoViaHTTP(cl client.HTTPClient, val string) (string, error) { return result.Unwrap().(*ResultEcho).Value, nil } +func echoIntViaHTTP(cl client.HTTPClient, val int) (int, error) { + params := map[string]interface{}{ + "arg": val, + } + var result Result + if _, err := cl.Call("echo_int", params, &result); err != nil { + return 0, err + } + return result.Unwrap().(*ResultEchoInt).Value, nil +} + func echoBytesViaHTTP(cl client.HTTPClient, bytes []byte) ([]byte, error) { params := map[string]interface{}{ "arg": bytes, @@ -150,6 +181,17 @@ func echoBytesViaHTTP(cl client.HTTPClient, bytes []byte) ([]byte, error) { return result.Unwrap().(*ResultEchoBytes).Value, nil } +func echoDataBytesViaHTTP(cl client.HTTPClient, bytes data.Bytes) (data.Bytes, error) { + params := map[string]interface{}{ + "arg": bytes, + } + var result Result + if _, err := cl.Call("echo_data_bytes", params, &result); err != nil { + return []byte{}, err + } + return result.Unwrap().(*ResultEchoDataBytes).Value, nil +} + func testWithHTTPClient(t *testing.T, cl client.HTTPClient) { val := "acbd" got, err := echoViaHTTP(cl, val) @@ -160,6 +202,18 @@ func testWithHTTPClient(t *testing.T, cl client.HTTPClient) { got2, err := echoBytesViaHTTP(cl, val2) require.Nil(t, err) assert.Equal(t, got2, val2) + + val3 := data.Bytes(randBytes(t)) + got3, err := echoDataBytesViaHTTP(cl, val3) + require.Nil(t, err) + assert.Equal(t, got3, val3) + + /* + val4 := rand.Intn(10000) + got4, err := echoIntViaHTTP(cl, val4) + require.Nil(t, err) + assert.Equal(t, got4, val4) + */ } func echoViaWS(cl *client.WSClient, val string) (string, error) { diff --git a/rpc/lib/server/handlers.go b/rpc/lib/server/handlers.go index 27451c23d..1c25d9e75 100644 --- a/rpc/lib/server/handlers.go +++ b/rpc/lib/server/handlers.go @@ -2,6 +2,7 @@ package rpcserver import ( "bytes" + "encoding/base64" "encoding/hex" "encoding/json" "fmt" @@ -14,7 +15,8 @@ import ( "github.com/gorilla/websocket" "github.com/pkg/errors" - wire "github.com/tendermint/go-wire" + //wire "github.com/tendermint/go-wire" + "github.com/tendermint/go-wire/data" types "github.com/tendermint/tendermint/rpc/lib/types" cmn "github.com/tendermint/tmlibs/common" events "github.com/tendermint/tmlibs/events" @@ -204,7 +206,32 @@ func jsonParamsToArgsWS(rpcFunc *RPCFunc, paramsI interface{}, wsCtx types.WSRPC func _jsonObjectToArg(ty reflect.Type, object interface{}) (reflect.Value, error) { var err error v := reflect.New(ty) - wire.ReadJSONObjectPtr(v.Interface(), object, &err) + + // if the object is a byte array, we need to decode it + if ty.Kind() == reflect.Slice && ty.Elem().Kind() == reflect.Uint8 { + s, ok := object.(string) + if !ok { + return v, fmt.Errorf("cmah") + } + + // if its data.Bytes, use hex + // else use base64 + dbty := reflect.TypeOf(data.Bytes{}) + if ty == dbty { + decoded, err := hex.DecodeString(s) + if err != nil { + return v, err + } + object = decoded + } else { + decoded, err := base64.StdEncoding.DecodeString(s) + if err != nil { + return v, err + } + object = decoded + } + } + v.Elem().Set(reflect.ValueOf(object)) if err != nil { return v, err } From 257f45b768d9fba1f7acf678c4fc1c847d017dab Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 28 Apr 2017 22:01:46 +0200 Subject: [PATCH 11/23] ebuchman: added some demos on how to parse unknown types --- rpc/lib/server/parse_test.go | 133 +++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 rpc/lib/server/parse_test.go diff --git a/rpc/lib/server/parse_test.go b/rpc/lib/server/parse_test.go new file mode 100644 index 000000000..5b0478b64 --- /dev/null +++ b/rpc/lib/server/parse_test.go @@ -0,0 +1,133 @@ +package rpcserver + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tendermint/go-wire/data" +) + +func TestParseJSONMap(t *testing.T) { + assert := assert.New(t) + + input := []byte(`{"value":"1234","height":22}`) + + // naive is float,string + var p1 map[string]interface{} + err := json.Unmarshal(input, &p1) + if assert.Nil(err) { + h, ok := p1["height"].(float64) + if assert.True(ok, "%#v", p1["height"]) { + assert.EqualValues(22, h) + } + v, ok := p1["value"].(string) + if assert.True(ok, "%#v", p1["value"]) { + assert.EqualValues("1234", v) + } + } + + // preloading map with values doesn't help + tmp := 0 + p2 := map[string]interface{}{ + "value": &data.Bytes{}, + "height": &tmp, + } + err = json.Unmarshal(input, &p2) + if assert.Nil(err) { + h, ok := p2["height"].(float64) + if assert.True(ok, "%#v", p2["height"]) { + assert.EqualValues(22, h) + } + v, ok := p2["value"].(string) + if assert.True(ok, "%#v", p2["value"]) { + assert.EqualValues("1234", v) + } + } + + // preload here with *pointers* to the desired types + // struct has unknown types, but hard-coded keys + tmp = 0 + p3 := struct { + Value interface{} `json:"value"` + Height interface{} `json:"height"` + }{ + Height: &tmp, + Value: &data.Bytes{}, + } + err = json.Unmarshal(input, &p3) + if assert.Nil(err) { + h, ok := p3.Height.(*int) + if assert.True(ok, "%#v", p3.Height) { + assert.Equal(22, *h) + } + v, ok := p3.Value.(*data.Bytes) + if assert.True(ok, "%#v", p3.Value) { + assert.EqualValues([]byte{0x12, 0x34}, *v) + } + } + + // simplest solution, but hard-coded + p4 := struct { + Value data.Bytes `json:"value"` + Height int `json:"height"` + }{} + err = json.Unmarshal(input, &p4) + if assert.Nil(err) { + assert.EqualValues(22, p4.Height) + assert.EqualValues([]byte{0x12, 0x34}, p4.Value) + } + + // so, let's use this trick... + // dynamic keys on map, and we can deserialize to the desired types + var p5 map[string]*json.RawMessage + err = json.Unmarshal(input, &p5) + if assert.Nil(err) { + var h int + err = json.Unmarshal(*p5["height"], &h) + if assert.Nil(err) { + assert.Equal(22, h) + } + + var v data.Bytes + err = json.Unmarshal(*p5["value"], &v) + if assert.Nil(err) { + assert.Equal(data.Bytes{0x12, 0x34}, v) + } + } +} + +func TestParseJSONArray(t *testing.T) { + assert := assert.New(t) + + input := []byte(`["1234",22]`) + + // naive is float,string + var p1 []interface{} + err := json.Unmarshal(input, &p1) + if assert.Nil(err) { + v, ok := p1[0].(string) + if assert.True(ok, "%#v", p1[0]) { + assert.EqualValues("1234", v) + } + h, ok := p1[1].(float64) + if assert.True(ok, "%#v", p1[1]) { + assert.EqualValues(22, h) + } + } + + // preloading map with values helps here (unlike map - p2 above) + tmp := 0 + p2 := []interface{}{&data.Bytes{}, &tmp} + err = json.Unmarshal(input, &p2) + if assert.Nil(err) { + v, ok := p2[0].(*data.Bytes) + if assert.True(ok, "%#v", p2[0]) { + assert.EqualValues([]byte{0x12, 0x34}, *v) + } + h, ok := p2[1].(*int) + if assert.True(ok, "%#v", p2[1]) { + assert.EqualValues(22, *h) + } + } +} From ac28b12fa8c513cf8fda42e4ae5064a0225985c0 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 28 Apr 2017 17:55:57 -0400 Subject: [PATCH 12/23] add readReflectJSON from wire --- rpc/lib/server/handlers.go | 29 +--- rpc/lib/server/wire.go | 290 +++++++++++++++++++++++++++++++++++++ 2 files changed, 291 insertions(+), 28 deletions(-) create mode 100644 rpc/lib/server/wire.go diff --git a/rpc/lib/server/handlers.go b/rpc/lib/server/handlers.go index 1c25d9e75..51eaf1401 100644 --- a/rpc/lib/server/handlers.go +++ b/rpc/lib/server/handlers.go @@ -2,7 +2,6 @@ package rpcserver import ( "bytes" - "encoding/base64" "encoding/hex" "encoding/json" "fmt" @@ -16,7 +15,6 @@ import ( "github.com/gorilla/websocket" "github.com/pkg/errors" //wire "github.com/tendermint/go-wire" - "github.com/tendermint/go-wire/data" types "github.com/tendermint/tendermint/rpc/lib/types" cmn "github.com/tendermint/tmlibs/common" events "github.com/tendermint/tmlibs/events" @@ -206,32 +204,7 @@ func jsonParamsToArgsWS(rpcFunc *RPCFunc, paramsI interface{}, wsCtx types.WSRPC func _jsonObjectToArg(ty reflect.Type, object interface{}) (reflect.Value, error) { var err error v := reflect.New(ty) - - // if the object is a byte array, we need to decode it - if ty.Kind() == reflect.Slice && ty.Elem().Kind() == reflect.Uint8 { - s, ok := object.(string) - if !ok { - return v, fmt.Errorf("cmah") - } - - // if its data.Bytes, use hex - // else use base64 - dbty := reflect.TypeOf(data.Bytes{}) - if ty == dbty { - decoded, err := hex.DecodeString(s) - if err != nil { - return v, err - } - object = decoded - } else { - decoded, err := base64.StdEncoding.DecodeString(s) - if err != nil { - return v, err - } - object = decoded - } - } - v.Elem().Set(reflect.ValueOf(object)) + readJSONObjectPtr(v.Interface(), object, &err) if err != nil { return v, err } diff --git a/rpc/lib/server/wire.go b/rpc/lib/server/wire.go new file mode 100644 index 000000000..ad3d61f8c --- /dev/null +++ b/rpc/lib/server/wire.go @@ -0,0 +1,290 @@ +package rpcserver + +import ( + "encoding/base64" + "encoding/hex" + "reflect" + "time" + + "github.com/pkg/errors" + + "github.com/tendermint/go-wire" + "github.com/tendermint/go-wire/data" + cmn "github.com/tendermint/tmlibs/common" +) + +var ( + timeType = wire.GetTypeFromStructDeclaration(struct{ time.Time }{}) +) + +func readJSONObjectPtr(o interface{}, object interface{}, err *error) interface{} { + rv, rt := reflect.ValueOf(o), reflect.TypeOf(o) + if rv.Kind() == reflect.Ptr { + readReflectJSON(rv.Elem(), rt.Elem(), wire.Options{}, object, err) + } else { + cmn.PanicSanity("ReadJSON(Object)Ptr expects o to be a pointer") + } + return o +} + +func readByteJSON(o interface{}) (typeByte byte, rest interface{}, err error) { + oSlice, ok := o.([]interface{}) + if !ok { + err = errors.New(cmn.Fmt("Expected type [Byte,?] but got type %v", reflect.TypeOf(o))) + return + } + if len(oSlice) != 2 { + err = errors.New(cmn.Fmt("Expected [Byte,?] len 2 but got len %v", len(oSlice))) + return + } + typeByte_, ok := oSlice[0].(float64) + typeByte = byte(typeByte_) + rest = oSlice[1] + return +} + +// Contract: Caller must ensure that rt is supported +// (e.g. is recursively composed of supported native types, and structs and slices.) +// rv and rt refer to the object we're unmarhsaling into, whereas o is the result of naiive json unmarshal (map[string]interface{}) +func readReflectJSON(rv reflect.Value, rt reflect.Type, opts wire.Options, o interface{}, err *error) { + + // Get typeInfo + typeInfo := wire.GetTypeInfo(rt) + + if rt.Kind() == reflect.Interface { + if !typeInfo.IsRegisteredInterface { + // There's no way we can read such a thing. + *err = errors.New(cmn.Fmt("Cannot read unregistered interface type %v", rt)) + return + } + if o == nil { + return // nil + } + typeByte, rest, err_ := readByteJSON(o) + if err_ != nil { + *err = err_ + return + } + crt, ok := typeInfo.ByteToType[typeByte] + if !ok { + *err = errors.New(cmn.Fmt("Byte %X not registered for interface %v", typeByte, rt)) + return + } + if crt.Kind() == reflect.Ptr { + crt = crt.Elem() + crv := reflect.New(crt) + readReflectJSON(crv.Elem(), crt, opts, rest, err) + rv.Set(crv) // NOTE: orig rv is ignored. + } else { + crv := reflect.New(crt).Elem() + readReflectJSON(crv, crt, opts, rest, err) + rv.Set(crv) // NOTE: orig rv is ignored. + } + return + } + + if rt.Kind() == reflect.Ptr { + if o == nil { + return // nil + } + // Create new struct if rv is nil. + if rv.IsNil() { + newRv := reflect.New(rt.Elem()) + rv.Set(newRv) + rv = newRv + } + // Dereference pointer + rv, rt = rv.Elem(), rt.Elem() + typeInfo = wire.GetTypeInfo(rt) + // continue... + } + + switch rt.Kind() { + case reflect.Array: + elemRt := rt.Elem() + length := rt.Len() + if elemRt.Kind() == reflect.Uint8 { + // Special case: Bytearrays + oString, ok := o.(string) + if !ok { + *err = errors.New(cmn.Fmt("Expected string but got type %v", reflect.TypeOf(o))) + return + } + + // if its data.Bytes, use hex; else use base64 + dbty := reflect.TypeOf(data.Bytes{}) + var buf []byte + var err_ error + if rt == dbty { + buf, err_ = hex.DecodeString(oString) + } else { + buf, err_ = base64.StdEncoding.DecodeString(oString) + } + if err_ != nil { + *err = err_ + return + } + if len(buf) != length { + *err = errors.New(cmn.Fmt("Expected bytearray of length %v but got %v", length, len(buf))) + return + } + //log.Info("Read bytearray", "bytes", buf) + reflect.Copy(rv, reflect.ValueOf(buf)) + } else { + oSlice, ok := o.([]interface{}) + if !ok { + *err = errors.New(cmn.Fmt("Expected array of %v but got type %v", rt, reflect.TypeOf(o))) + return + } + if len(oSlice) != length { + *err = errors.New(cmn.Fmt("Expected array of length %v but got %v", length, len(oSlice))) + return + } + for i := 0; i < length; i++ { + elemRv := rv.Index(i) + readReflectJSON(elemRv, elemRt, opts, oSlice[i], err) + } + //log.Info("Read x-array", "x", elemRt, "length", length) + } + + case reflect.Slice: + elemRt := rt.Elem() + if elemRt.Kind() == reflect.Uint8 { + // Special case: Byteslices + oString, ok := o.(string) + if !ok { + *err = errors.New(cmn.Fmt("Expected string but got type %v", reflect.TypeOf(o))) + return + } + // if its data.Bytes, use hex; else use base64 + dbty := reflect.TypeOf(data.Bytes{}) + var buf []byte + var err_ error + if rt == dbty { + buf, err_ = hex.DecodeString(oString) + } else { + buf, err_ = base64.StdEncoding.DecodeString(oString) + } + if err_ != nil { + *err = err_ + return + } + //log.Info("Read byteslice", "bytes", byteslice) + rv.Set(reflect.ValueOf(buf)) + } else { + // Read length + oSlice, ok := o.([]interface{}) + if !ok { + *err = errors.New(cmn.Fmt("Expected array of %v but got type %v", rt, reflect.TypeOf(o))) + return + } + length := len(oSlice) + //log.Info("Read slice", "length", length) + sliceRv := reflect.MakeSlice(rt, length, length) + // Read elems + for i := 0; i < length; i++ { + elemRv := sliceRv.Index(i) + readReflectJSON(elemRv, elemRt, opts, oSlice[i], err) + } + rv.Set(sliceRv) + } + + case reflect.Struct: + if rt == timeType { + // Special case: time.Time + str, ok := o.(string) + if !ok { + *err = errors.New(cmn.Fmt("Expected string but got type %v", reflect.TypeOf(o))) + return + } + // try three ways, seconds, milliseconds, or microseconds... + t, err_ := time.Parse(time.RFC3339Nano, str) + if err_ != nil { + *err = err_ + return + } + rv.Set(reflect.ValueOf(t)) + } else { + if typeInfo.Unwrap { + f := typeInfo.Fields[0] + fieldIdx, fieldType, opts := f.Index, f.Type, f.Options + fieldRv := rv.Field(fieldIdx) + readReflectJSON(fieldRv, fieldType, opts, o, err) + } else { + oMap, ok := o.(map[string]interface{}) + if !ok { + *err = errors.New(cmn.Fmt("Expected map but got type %v", reflect.TypeOf(o))) + return + } + // TODO: ensure that all fields are set? + // TODO: disallow unknown oMap fields? + for _, fieldInfo := range typeInfo.Fields { + f := fieldInfo + fieldIdx, fieldType, opts := f.Index, f.Type, f.Options + value, ok := oMap[opts.JSONName] + if !ok { + continue // Skip missing fields. + } + fieldRv := rv.Field(fieldIdx) + readReflectJSON(fieldRv, fieldType, opts, value, err) + } + } + } + + case reflect.String: + str, ok := o.(string) + if !ok { + *err = errors.New(cmn.Fmt("Expected string but got type %v", reflect.TypeOf(o))) + return + } + //log.Info("Read string", "str", str) + rv.SetString(str) + + case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int: + num, ok := o.(float64) + if !ok { + *err = errors.New(cmn.Fmt("Expected numeric but got type %v", reflect.TypeOf(o))) + return + } + //log.Info("Read num", "num", num) + rv.SetInt(int64(num)) + + case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint: + num, ok := o.(float64) + if !ok { + *err = errors.New(cmn.Fmt("Expected numeric but got type %v", reflect.TypeOf(o))) + return + } + if num < 0 { + *err = errors.New(cmn.Fmt("Expected unsigned numeric but got %v", num)) + return + } + //log.Info("Read num", "num", num) + rv.SetUint(uint64(num)) + + case reflect.Float64, reflect.Float32: + if !opts.Unsafe { + *err = errors.New("Wire float* support requires `wire:\"unsafe\"`") + return + } + num, ok := o.(float64) + if !ok { + *err = errors.New(cmn.Fmt("Expected numeric but got type %v", reflect.TypeOf(o))) + return + } + //log.Info("Read num", "num", num) + rv.SetFloat(num) + + case reflect.Bool: + bl, ok := o.(bool) + if !ok { + *err = errors.New(cmn.Fmt("Expected boolean but got type %v", reflect.TypeOf(o))) + return + } + //log.Info("Read boolean", "boolean", bl) + rv.SetBool(bl) + + default: + cmn.PanicSanity(cmn.Fmt("Unknown field type %v", rt.Kind())) + } +} From 07e59e63f90c8bee56ba1b260f940d49c1cbf463 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 28 Apr 2017 17:57:06 -0400 Subject: [PATCH 13/23] TMEventDataInner --- consensus/common_test.go | 2 +- consensus/mempool_test.go | 2 +- consensus/reactor.go | 8 ++-- consensus/reactor_test.go | 6 +-- consensus/state_test.go | 30 +++++++------- rpc/client/event_test.go | 4 +- rpc/client/helpers.go | 6 +-- rpc/core/mempool.go | 2 +- rpc/lib/rpc_test.go | 10 ++--- rpc/test/client_test.go | 7 ++-- rpc/test/helpers.go | 2 + types/events.go | 83 +++++++++++++++++++++++++++------------ 12 files changed, 98 insertions(+), 64 deletions(-) diff --git a/consensus/common_test.go b/consensus/common_test.go index 469fc9c6b..1443232a6 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -206,7 +206,7 @@ func subscribeToVoter(cs *ConsensusState, addr []byte) chan interface{} { go func() { for { v := <-voteCh0 - vote := v.(types.EventDataVote) + vote := v.(types.TMEventData).Unwrap().(types.EventDataVote) // we only fire for our own votes if bytes.Equal(addr, vote.Vote.ValidatorAddress) { voteCh <- v diff --git a/consensus/mempool_test.go b/consensus/mempool_test.go index 8808185da..15e6a1550 100644 --- a/consensus/mempool_test.go +++ b/consensus/mempool_test.go @@ -44,7 +44,7 @@ func TestTxConcurrentWithCommit(t *testing.T) { for nTxs := 0; nTxs < NTxs; { select { case b := <-newBlockCh: - nTxs += b.(types.EventDataNewBlock).Block.Header.NumTxs + nTxs += b.(types.TMEventData).Unwrap().(types.EventDataNewBlock).Block.Header.NumTxs case <-ticker.C: panic("Timed out waiting to commit blocks with transactions") } diff --git a/consensus/reactor.go b/consensus/reactor.go index 5fe45cc01..5734298ef 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -8,11 +8,11 @@ import ( "sync" "time" - . "github.com/tendermint/tmlibs/common" - "github.com/tendermint/tendermint/p2p" "github.com/tendermint/go-wire" + "github.com/tendermint/tendermint/p2p" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" + . "github.com/tendermint/tmlibs/common" ) const ( @@ -299,12 +299,12 @@ func (conR *ConsensusReactor) SetEventSwitch(evsw types.EventSwitch) { func (conR *ConsensusReactor) registerEventCallbacks() { types.AddListenerForEvent(conR.evsw, "conR", types.EventStringNewRoundStep(), func(data types.TMEventData) { - rs := data.(types.EventDataRoundState).RoundState.(*RoundState) + rs := data.Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) conR.broadcastNewRoundStep(rs) }) types.AddListenerForEvent(conR.evsw, "conR", types.EventStringVote(), func(data types.TMEventData) { - edv := data.(types.EventDataVote) + edv := data.Unwrap().(types.EventDataVote) conR.broadcastHasVoteMessage(edv.Vote) }) } diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index 66827c155..fdf590151 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -8,10 +8,10 @@ import ( "github.com/tendermint/tendermint/config/tendermint_test" - "github.com/tendermint/tmlibs/events" + "github.com/tendermint/abci/example/dummy" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/types" - "github.com/tendermint/abci/example/dummy" + "github.com/tendermint/tmlibs/events" ) func init() { @@ -252,7 +252,7 @@ func TestReactorWithTimeoutCommit(t *testing.T) { func waitForAndValidateBlock(t *testing.T, n int, activeVals map[string]struct{}, eventChans []chan interface{}, css []*ConsensusState, txs ...[]byte) { timeoutWaitGroup(t, n, func(wg *sync.WaitGroup, j int) { newBlockI := <-eventChans[j] - newBlock := newBlockI.(types.EventDataNewBlock).Block + newBlock := newBlockI.(types.TMEventData).Unwrap().(types.EventDataNewBlock).Block log.Warn("Got block", "height", newBlock.Height, "validator", j) err := validateBlock(newBlock, activeVals) if err != nil { diff --git a/consensus/state_test.go b/consensus/state_test.go index d2d34e3ad..2d5ed410b 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - . "github.com/tendermint/tmlibs/common" "github.com/tendermint/tendermint/config/tendermint_test" "github.com/tendermint/tendermint/types" + . "github.com/tendermint/tmlibs/common" ) func init() { @@ -248,7 +248,7 @@ func TestFullRound1(t *testing.T) { // grab proposal re := <-propCh - propBlockHash := re.(types.EventDataRoundState).RoundState.(*RoundState).ProposalBlock.Hash() + propBlockHash := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState).ProposalBlock.Hash() <-voteCh // wait for prevote // NOTE: voteChan cap of 0 ensures we can complete this @@ -345,7 +345,7 @@ func TestLockNoPOL(t *testing.T) { cs1.startRoutines(0) re := <-proposalCh - rs := re.(types.EventDataRoundState).RoundState.(*RoundState) + rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) theBlockHash := rs.ProposalBlock.Hash() <-voteCh // prevote @@ -385,7 +385,7 @@ func TestLockNoPOL(t *testing.T) { // now we're on a new round and not the proposer, so wait for timeout re = <-timeoutProposeCh - rs = re.(types.EventDataRoundState).RoundState.(*RoundState) + rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) if rs.ProposalBlock != nil { panic("Expected proposal block to be nil") @@ -429,7 +429,7 @@ func TestLockNoPOL(t *testing.T) { incrementRound(vs2) re = <-proposalCh - rs = re.(types.EventDataRoundState).RoundState.(*RoundState) + rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) // now we're on a new round and are the proposer if !bytes.Equal(rs.ProposalBlock.Hash(), rs.LockedBlock.Hash()) { @@ -518,7 +518,7 @@ func TestLockPOLRelock(t *testing.T) { <-newRoundCh re := <-proposalCh - rs := re.(types.EventDataRoundState).RoundState.(*RoundState) + rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) theBlockHash := rs.ProposalBlock.Hash() <-voteCh // prevote @@ -589,9 +589,9 @@ func TestLockPOLRelock(t *testing.T) { _, _ = <-voteCh, <-voteCh be := <-newBlockCh - b := be.(types.EventDataNewBlockHeader) + b := be.(types.TMEventData).Unwrap().(types.EventDataNewBlockHeader) re = <-newRoundCh - rs = re.(types.EventDataRoundState).RoundState.(*RoundState) + rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) if rs.Height != 2 { panic("Expected height to increment") } @@ -627,7 +627,7 @@ func TestLockPOLUnlock(t *testing.T) { startTestRound(cs1, cs1.Height, 0) <-newRoundCh re := <-proposalCh - rs := re.(types.EventDataRoundState).RoundState.(*RoundState) + rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) theBlockHash := rs.ProposalBlock.Hash() <-voteCh // prevote @@ -653,7 +653,7 @@ func TestLockPOLUnlock(t *testing.T) { // timeout to new round re = <-timeoutWaitCh - rs = re.(types.EventDataRoundState).RoundState.(*RoundState) + rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) lockedBlockHash := rs.LockedBlock.Hash() //XXX: this isnt gauranteed to get there before the timeoutPropose ... @@ -713,7 +713,7 @@ func TestLockPOLSafety1(t *testing.T) { startTestRound(cs1, cs1.Height, 0) <-newRoundCh re := <-proposalCh - rs := re.(types.EventDataRoundState).RoundState.(*RoundState) + rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) propBlock := rs.ProposalBlock <-voteCh // prevote @@ -761,7 +761,7 @@ func TestLockPOLSafety1(t *testing.T) { re = <-proposalCh } - rs = re.(types.EventDataRoundState).RoundState.(*RoundState) + rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) if rs.LockedBlock != nil { panic("we should not be locked!") @@ -1009,7 +1009,7 @@ func TestHalt1(t *testing.T) { startTestRound(cs1, cs1.Height, 0) <-newRoundCh re := <-proposalCh - rs := re.(types.EventDataRoundState).RoundState.(*RoundState) + rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) propBlock := rs.ProposalBlock propBlockParts := propBlock.MakePartSet(partSize) @@ -1032,7 +1032,7 @@ func TestHalt1(t *testing.T) { // timeout to new round <-timeoutWaitCh re = <-newRoundCh - rs = re.(types.EventDataRoundState).RoundState.(*RoundState) + rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) log.Notice("### ONTO ROUND 1") /*Round2 @@ -1050,7 +1050,7 @@ func TestHalt1(t *testing.T) { // receiving that precommit should take us straight to commit <-newBlockCh re = <-newRoundCh - rs = re.(types.EventDataRoundState).RoundState.(*RoundState) + rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) if rs.Height != 2 { panic("expected height to increment") diff --git a/rpc/client/event_test.go b/rpc/client/event_test.go index cc421ad90..1b99854cb 100644 --- a/rpc/client/event_test.go +++ b/rpc/client/event_test.go @@ -25,7 +25,7 @@ func TestHeaderEvents(t *testing.T) { evtTyp := types.EventStringNewBlockHeader() evt, err := client.WaitForOneEvent(c, evtTyp, 1*time.Second) require.Nil(err, "%d: %+v", i, err) - _, ok := evt.(types.EventDataNewBlockHeader) + _, ok := evt.Unwrap().(types.EventDataNewBlockHeader) require.True(ok, "%d: %#v", i, evt) // TODO: more checks... } @@ -56,7 +56,7 @@ func TestTxEvents(t *testing.T) { evt, err := client.WaitForOneEvent(c, evtTyp, 1*time.Second) require.Nil(err, "%d: %+v", i, err) // and make sure it has the proper info - txe, ok := evt.(types.EventDataTx) + txe, ok := evt.Unwrap().(types.EventDataTx) require.True(ok, "%d: %#v", i, evt) // make sure this is the proper tx require.EqualValues(tx, txe.Tx) diff --git a/rpc/client/helpers.go b/rpc/client/helpers.go index 330bcd199..bc26ea57f 100644 --- a/rpc/client/helpers.go +++ b/rpc/client/helpers.go @@ -4,9 +4,9 @@ import ( "time" "github.com/pkg/errors" + "github.com/tendermint/tendermint/types" cmn "github.com/tendermint/tmlibs/common" events "github.com/tendermint/tmlibs/events" - "github.com/tendermint/tendermint/types" ) // Waiter is informed of current height, decided whether to quit early @@ -77,12 +77,12 @@ func WaitForOneEvent(evsw types.EventSwitch, select { case <-quit: - return nil, errors.New("timed out waiting for event") + return types.TMEventData{}, errors.New("timed out waiting for event") case evt := <-evts: tmevt, ok := evt.(types.TMEventData) if ok { return tmevt, nil } - return nil, errors.Errorf("Got unexpected event type: %#v", evt) + return types.TMEventData{}, errors.Errorf("Got unexpected event type: %#v", evt) } } diff --git a/rpc/core/mempool.go b/rpc/core/mempool.go index 4c1b54cfc..a6e7093fd 100644 --- a/rpc/core/mempool.go +++ b/rpc/core/mempool.go @@ -50,7 +50,7 @@ func BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { // subscribe to tx being committed in block deliverTxResCh := make(chan types.EventDataTx, 1) types.AddListenerForEvent(eventSwitch, "rpc", types.EventStringTx(tx), func(data types.TMEventData) { - deliverTxResCh <- data.(types.EventDataTx) + deliverTxResCh <- data.Unwrap().(types.EventDataTx) }) // broadcast the tx and register checktx callback diff --git a/rpc/lib/rpc_test.go b/rpc/lib/rpc_test.go index 2f61f3887..9cbdc29a6 100644 --- a/rpc/lib/rpc_test.go +++ b/rpc/lib/rpc_test.go @@ -208,12 +208,10 @@ func testWithHTTPClient(t *testing.T, cl client.HTTPClient) { require.Nil(t, err) assert.Equal(t, got3, val3) - /* - val4 := rand.Intn(10000) - got4, err := echoIntViaHTTP(cl, val4) - require.Nil(t, err) - assert.Equal(t, got4, val4) - */ + val4 := rand.Intn(10000) + got4, err := echoIntViaHTTP(cl, val4) + require.Nil(t, err) + assert.Equal(t, got4, val4) } func echoViaWS(cl *client.WSClient, val string) (string, error) { diff --git a/rpc/test/client_test.go b/rpc/test/client_test.go index c9a922d72..8e636c333 100644 --- a/rpc/test/client_test.go +++ b/rpc/test/client_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" abci "github.com/tendermint/abci/types" + "github.com/tendermint/go-wire/data" "github.com/tendermint/tendermint/rpc/core" ctypes "github.com/tendermint/tendermint/rpc/core/types" rpc "github.com/tendermint/tendermint/rpc/lib/client" @@ -116,7 +117,7 @@ func testABCIQuery(t *testing.T, client rpc.HTTPClient) { time.Sleep(time.Millisecond * 500) tmResult := new(ctypes.TMResult) _, err := client.Call("abci_query", - map[string]interface{}{"path": "", "data": k, "prove": false}, tmResult) + map[string]interface{}{"path": "", "data": data.Bytes(k), "prove": false}, tmResult) require.Nil(t, err) resQuery := tmResult.Unwrap().(*ctypes.ResultABCIQuery) @@ -286,7 +287,7 @@ func TestWSBlockchainGrowth(t *testing.T) { var initBlockN int for i := 0; i < 3; i++ { waitForEvent(t, wsc, eid, true, func() {}, func(eid string, eventData interface{}) error { - block := eventData.(types.EventDataNewBlock).Block + block := eventData.(types.TMEventData).Unwrap().(types.EventDataNewBlock).Block if i == 0 { initBlockN = block.Header.Height } else { @@ -320,7 +321,7 @@ func TestWSTxEvent(t *testing.T) { require.Nil(err) waitForEvent(t, wsc, eid, true, func() {}, func(eid string, b interface{}) error { - evt, ok := b.(types.EventDataTx) + evt, ok := b.(types.TMEventData).Unwrap().(types.EventDataTx) require.True(ok, "Got wrong event type: %#v", b) require.Equal(tx, []byte(evt.Tx), "Returned different tx") require.Equal(abci.CodeType_OK, evt.Code) diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index 76d04f511..2b01d0a69 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -131,9 +131,11 @@ func waitForEvent(t *testing.T, wsc *client.WSClient, eventid string, dieOnTimeo for { select { case r := <-wsc.ResultsCh: + fmt.Println("GOT IT", string(r)) result := new(ctypes.TMResult) err = json.Unmarshal(r, result) if err != nil { + fmt.Println("POOP", err) errCh <- err break LOOP } diff --git a/types/events.go b/types/events.go index 953859d0d..8c29c4445 100644 --- a/types/events.go +++ b/types/events.go @@ -3,7 +3,6 @@ package types import ( // for registering TMEventData as events.EventData abci "github.com/tendermint/abci/types" - "github.com/tendermint/go-wire" "github.com/tendermint/go-wire/data" cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/events" @@ -34,10 +33,47 @@ func EventStringVote() string { return "Vote" } //---------------------------------------- +var ( + EventDataNameNewBlock = "new_block" + EventDataNameNewBlockHeader = "new_block_header" + EventDataNameTx = "tx" + EventDataNameRoundState = "round_state" + EventDataNameVote = "vote" +) + +//---------------------------------------- + // implements events.EventData -type TMEventData interface { +type TMEventDataInner interface { events.EventData - AssertIsTMEventData() +} + +type TMEventData struct { + TMEventDataInner `json:"unwrap"` +} + +func (tmr TMEventData) MarshalJSON() ([]byte, error) { + return tmEventDataMapper.ToJSON(tmr.TMEventDataInner) +} + +func (tmr *TMEventData) UnmarshalJSON(data []byte) (err error) { + parsed, err := tmEventDataMapper.FromJSON(data) + if err == nil && parsed != nil { + tmr.TMEventDataInner = parsed.(TMEventDataInner) + } + return +} + +func (tmr TMEventData) Unwrap() TMEventDataInner { + tmrI := tmr.TMEventDataInner + for wrap, ok := tmrI.(TMEventData); ok; wrap, ok = tmrI.(TMEventData) { + tmrI = wrap.TMEventDataInner + } + return tmrI +} + +func (tmr TMEventData) Empty() bool { + return tmr.TMEventDataInner == nil } const ( @@ -50,15 +86,12 @@ const ( EventDataTypeVote = byte(0x12) ) -var _ = wire.RegisterInterface( - struct{ TMEventData }{}, - wire.ConcreteType{EventDataNewBlock{}, EventDataTypeNewBlock}, - wire.ConcreteType{EventDataNewBlockHeader{}, EventDataTypeNewBlockHeader}, - // wire.ConcreteType{EventDataFork{}, EventDataTypeFork }, - wire.ConcreteType{EventDataTx{}, EventDataTypeTx}, - wire.ConcreteType{EventDataRoundState{}, EventDataTypeRoundState}, - wire.ConcreteType{EventDataVote{}, EventDataTypeVote}, -) +var tmEventDataMapper = data.NewMapper(TMEventData{}). + RegisterImplementation(EventDataNewBlock{}, EventDataNameNewBlock, EventDataTypeNewBlock). + RegisterImplementation(EventDataNewBlockHeader{}, EventDataNameNewBlockHeader, EventDataTypeNewBlockHeader). + RegisterImplementation(EventDataTx{}, EventDataNameTx, EventDataTypeTx). + RegisterImplementation(EventDataRoundState{}, EventDataNameRoundState, EventDataTypeRoundState). + RegisterImplementation(EventDataVote{}, EventDataNameVote, EventDataTypeVote) // Most event messages are basic types (a block, a transaction) // but some (an input to a call tx or a receive) are more exotic @@ -147,55 +180,55 @@ func AddListenerForEvent(evsw EventSwitch, id, event string, cb func(data TMEven //--- block, tx, and vote events func FireEventNewBlock(fireable events.Fireable, block EventDataNewBlock) { - fireEvent(fireable, EventStringNewBlock(), block) + fireEvent(fireable, EventStringNewBlock(), TMEventData{block}) } func FireEventNewBlockHeader(fireable events.Fireable, header EventDataNewBlockHeader) { - fireEvent(fireable, EventStringNewBlockHeader(), header) + fireEvent(fireable, EventStringNewBlockHeader(), TMEventData{header}) } func FireEventVote(fireable events.Fireable, vote EventDataVote) { - fireEvent(fireable, EventStringVote(), vote) + fireEvent(fireable, EventStringVote(), TMEventData{vote}) } func FireEventTx(fireable events.Fireable, tx EventDataTx) { - fireEvent(fireable, EventStringTx(tx.Tx), tx) + fireEvent(fireable, EventStringTx(tx.Tx), TMEventData{tx}) } //--- EventDataRoundState events func FireEventNewRoundStep(fireable events.Fireable, rs EventDataRoundState) { - fireEvent(fireable, EventStringNewRoundStep(), rs) + fireEvent(fireable, EventStringNewRoundStep(), TMEventData{rs}) } func FireEventTimeoutPropose(fireable events.Fireable, rs EventDataRoundState) { - fireEvent(fireable, EventStringTimeoutPropose(), rs) + fireEvent(fireable, EventStringTimeoutPropose(), TMEventData{rs}) } func FireEventTimeoutWait(fireable events.Fireable, rs EventDataRoundState) { - fireEvent(fireable, EventStringTimeoutWait(), rs) + fireEvent(fireable, EventStringTimeoutWait(), TMEventData{rs}) } func FireEventNewRound(fireable events.Fireable, rs EventDataRoundState) { - fireEvent(fireable, EventStringNewRound(), rs) + fireEvent(fireable, EventStringNewRound(), TMEventData{rs}) } func FireEventCompleteProposal(fireable events.Fireable, rs EventDataRoundState) { - fireEvent(fireable, EventStringCompleteProposal(), rs) + fireEvent(fireable, EventStringCompleteProposal(), TMEventData{rs}) } func FireEventPolka(fireable events.Fireable, rs EventDataRoundState) { - fireEvent(fireable, EventStringPolka(), rs) + fireEvent(fireable, EventStringPolka(), TMEventData{rs}) } func FireEventUnlock(fireable events.Fireable, rs EventDataRoundState) { - fireEvent(fireable, EventStringUnlock(), rs) + fireEvent(fireable, EventStringUnlock(), TMEventData{rs}) } func FireEventRelock(fireable events.Fireable, rs EventDataRoundState) { - fireEvent(fireable, EventStringRelock(), rs) + fireEvent(fireable, EventStringRelock(), TMEventData{rs}) } func FireEventLock(fireable events.Fireable, rs EventDataRoundState) { - fireEvent(fireable, EventStringLock(), rs) + fireEvent(fireable, EventStringLock(), TMEventData{rs}) } From 884060eb9bbbf36ec7a8d3d2235b0f7aa5b3ed81 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 28 Apr 2017 22:04:14 -0400 Subject: [PATCH 14/23] rpc/lib: no Result wrapper --- rpc/lib/rpc_test.go | 112 ++++++++++++++------------------------------ 1 file changed, 35 insertions(+), 77 deletions(-) diff --git a/rpc/lib/rpc_test.go b/rpc/lib/rpc_test.go index 9cbdc29a6..a14551f4f 100644 --- a/rpc/lib/rpc_test.go +++ b/rpc/lib/rpc_test.go @@ -29,59 +29,22 @@ const ( websocketEndpoint = "/websocket/endpoint" ) -// Define a type for results and register concrete versions -type ResultInner interface{} - -type Result struct { - ResultInner `json:"unwrap"` -} - -func (r Result) MarshalJSON() ([]byte, error) { - return resultMapper.ToJSON(r.ResultInner) -} - -func (r *Result) UnmarshalJSON(data []byte) (err error) { - parsed, err := resultMapper.FromJSON(data) - if err == nil && parsed != nil { - r.ResultInner = parsed.(ResultInner) - } - return -} - -func (r Result) Unwrap() ResultInner { - tmrI := r.ResultInner - for wrap, ok := tmrI.(Result); ok; wrap, ok = tmrI.(Result) { - tmrI = wrap.ResultInner - } - return tmrI -} - -func (r Result) Empty() bool { - return r.ResultInner == nil -} - type ResultEcho struct { - Value string + Value string `json:"value"` } type ResultEchoInt struct { - Value int + Value int `json:"value"` } type ResultEchoBytes struct { - Value []byte + Value []byte `json:"value"` } type ResultEchoDataBytes struct { - Value data.Bytes + Value data.Bytes `json:"value"` } -var resultMapper = data.NewMapper(Result{}). - RegisterImplementation(&ResultEcho{}, "echo", 0x1). - RegisterImplementation(&ResultEchoBytes{}, "echo_bytes", 0x2). - RegisterImplementation(&ResultEchoDataBytes{}, "echo_data_bytes", 0x3). - RegisterImplementation(&ResultEchoInt{}, "echo_int", 0x4) - // Define some routes var Routes = map[string]*server.RPCFunc{ "echo": server.NewRPCFunc(EchoResult, "arg"), @@ -91,24 +54,24 @@ var Routes = map[string]*server.RPCFunc{ "echo_int": server.NewRPCFunc(EchoIntResult, "arg"), } -func EchoResult(v string) (Result, error) { - return Result{&ResultEcho{v}}, nil +func EchoResult(v string) (*ResultEcho, error) { + return &ResultEcho{v}, nil } -func EchoWSResult(wsCtx types.WSRPCContext, v string) (Result, error) { - return Result{&ResultEcho{v}}, nil +func EchoWSResult(wsCtx types.WSRPCContext, v string) (*ResultEcho, error) { + return &ResultEcho{v}, nil } -func EchoIntResult(v int) (Result, error) { - return Result{&ResultEchoInt{v}}, nil +func EchoIntResult(v int) (*ResultEchoInt, error) { + return &ResultEchoInt{v}, nil } -func EchoBytesResult(v []byte) (Result, error) { - return Result{&ResultEchoBytes{v}}, nil +func EchoBytesResult(v []byte) (*ResultEchoBytes, error) { + return &ResultEchoBytes{v}, nil } -func EchoDataBytesResult(v data.Bytes) (Result, error) { - return Result{&ResultEchoDataBytes{v}}, nil +func EchoDataBytesResult(v data.Bytes) (*ResultEchoDataBytes, error) { + return &ResultEchoDataBytes{v}, nil } // launch unix and tcp servers @@ -152,44 +115,44 @@ func echoViaHTTP(cl client.HTTPClient, val string) (string, error) { params := map[string]interface{}{ "arg": val, } - var result Result - if _, err := cl.Call("echo", params, &result); err != nil { + result := new(ResultEcho) + if _, err := cl.Call("echo", params, result); err != nil { return "", err } - return result.Unwrap().(*ResultEcho).Value, nil + return result.Value, nil } func echoIntViaHTTP(cl client.HTTPClient, val int) (int, error) { params := map[string]interface{}{ "arg": val, } - var result Result - if _, err := cl.Call("echo_int", params, &result); err != nil { + result := new(ResultEchoInt) + if _, err := cl.Call("echo_int", params, result); err != nil { return 0, err } - return result.Unwrap().(*ResultEchoInt).Value, nil + return result.Value, nil } func echoBytesViaHTTP(cl client.HTTPClient, bytes []byte) ([]byte, error) { params := map[string]interface{}{ "arg": bytes, } - var result Result - if _, err := cl.Call("echo_bytes", params, &result); err != nil { + result := new(ResultEchoBytes) + if _, err := cl.Call("echo_bytes", params, result); err != nil { return []byte{}, err } - return result.Unwrap().(*ResultEchoBytes).Value, nil + return result.Value, nil } func echoDataBytesViaHTTP(cl client.HTTPClient, bytes data.Bytes) (data.Bytes, error) { params := map[string]interface{}{ "arg": bytes, } - var result Result - if _, err := cl.Call("echo_data_bytes", params, &result); err != nil { + result := new(ResultEchoDataBytes) + if _, err := cl.Call("echo_data_bytes", params, result); err != nil { return []byte{}, err } - return result.Unwrap().(*ResultEchoDataBytes).Value, nil + return result.Value, nil } func testWithHTTPClient(t *testing.T, cl client.HTTPClient) { @@ -225,12 +188,12 @@ func echoViaWS(cl *client.WSClient, val string) (string, error) { select { case msg := <-cl.ResultsCh: - result := new(Result) + result := new(ResultEcho) err = json.Unmarshal(msg, result) if err != nil { return "", nil } - return result.Unwrap().(*ResultEcho).Value, nil + return result.Value, nil case err := <-cl.ErrorsCh: return "", err } @@ -247,12 +210,12 @@ func echoBytesViaWS(cl *client.WSClient, bytes []byte) ([]byte, error) { select { case msg := <-cl.ResultsCh: - result := new(Result) + result := new(ResultEchoBytes) err = json.Unmarshal(msg, result) if err != nil { return []byte{}, nil } - return result.Unwrap().(*ResultEchoBytes).Value, nil + return result.Value, nil case err := <-cl.ErrorsCh: return []byte{}, err } @@ -320,20 +283,15 @@ func TestWSNewWSRPCFunc(t *testing.T) { params := map[string]interface{}{ "arg": val, } - err = cl.WriteJSON(types.RPCRequest{ - JSONRPC: "2.0", - ID: "", - Method: "echo_ws", - Params: params, - }) + err = cl.Call("echo_ws", params) require.Nil(t, err) select { case msg := <-cl.ResultsCh: - result := new(Result) + result := new(ResultEcho) err = json.Unmarshal(msg, result) require.Nil(t, err) - got := result.Unwrap().(*ResultEcho).Value + got := result.Value assert.Equal(t, got, val) case err := <-cl.ErrorsCh: t.Fatal(err) @@ -358,10 +316,10 @@ func TestWSHandlesArrayParams(t *testing.T) { select { case msg := <-cl.ResultsCh: - result := new(Result) + result := new(ResultEcho) err = json.Unmarshal(msg, result) require.Nil(t, err) - got := result.Unwrap().(*ResultEcho).Value + got := result.Value assert.Equal(t, got, val) case err := <-cl.ErrorsCh: t.Fatalf("%+v", err) From 4e781961e9b35e5de91bb7d7067afb1e3ebacaa9 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 28 Apr 2017 22:26:17 -0400 Subject: [PATCH 15/23] remove TMResult. ::drinks champagne:: --- rpc/client/httpclient.go | 88 +++++++++--------- rpc/core/events.go | 4 +- rpc/core/routes.go | 173 +++++------------------------------- rpc/core/tx.go | 3 + rpc/core/types/responses.go | 59 +----------- rpc/test/client_test.go | 79 ++++++++-------- rpc/test/helpers.go | 11 ++- 7 files changed, 112 insertions(+), 305 deletions(-) diff --git a/rpc/client/httpclient.go b/rpc/client/httpclient.go index 662ced895..cb7149406 100644 --- a/rpc/client/httpclient.go +++ b/rpc/client/httpclient.go @@ -50,42 +50,41 @@ func (c *HTTP) _assertIsEventSwitch() types.EventSwitch { } func (c *HTTP) Status() (*ctypes.ResultStatus, error) { - tmResult := new(ctypes.TMResult) - _, err := c.rpc.Call("status", map[string]interface{}{}, tmResult) + result := new(ctypes.ResultStatus) + _, err := c.rpc.Call("status", map[string]interface{}{}, result) if err != nil { return nil, errors.Wrap(err, "Status") } - // note: panics if rpc doesn't match. okay??? - return tmResult.Unwrap().(*ctypes.ResultStatus), nil + return result, nil } func (c *HTTP) ABCIInfo() (*ctypes.ResultABCIInfo, error) { - tmResult := new(ctypes.TMResult) - _, err := c.rpc.Call("abci_info", map[string]interface{}{}, tmResult) + result := new(ctypes.ResultABCIInfo) + _, err := c.rpc.Call("abci_info", map[string]interface{}{}, result) if err != nil { return nil, errors.Wrap(err, "ABCIInfo") } - return tmResult.Unwrap().(*ctypes.ResultABCIInfo), nil + return result, nil } func (c *HTTP) ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.ResultABCIQuery, error) { - tmResult := new(ctypes.TMResult) + result := new(ctypes.ResultABCIQuery) _, err := c.rpc.Call("abci_query", map[string]interface{}{"path": path, "data": data, "prove": prove}, - tmResult) + result) if err != nil { return nil, errors.Wrap(err, "ABCIQuery") } - return tmResult.Unwrap().(*ctypes.ResultABCIQuery), nil + return result, nil } func (c *HTTP) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { - tmResult := new(ctypes.TMResult) - _, err := c.rpc.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, tmResult) + result := new(ctypes.ResultBroadcastTxCommit) + _, err := c.rpc.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, result) if err != nil { return nil, errors.Wrap(err, "broadcast_tx_commit") } - return tmResult.Unwrap().(*ctypes.ResultBroadcastTxCommit), nil + return result, nil } func (c *HTTP) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { @@ -97,90 +96,90 @@ func (c *HTTP) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { } func (c *HTTP) broadcastTX(route string, tx types.Tx) (*ctypes.ResultBroadcastTx, error) { - tmResult := new(ctypes.TMResult) - _, err := c.rpc.Call(route, map[string]interface{}{"tx": tx}, tmResult) + result := new(ctypes.ResultBroadcastTx) + _, err := c.rpc.Call(route, map[string]interface{}{"tx": tx}, result) if err != nil { return nil, errors.Wrap(err, route) } - return tmResult.Unwrap().(*ctypes.ResultBroadcastTx), nil + return result, nil } func (c *HTTP) NetInfo() (*ctypes.ResultNetInfo, error) { - tmResult := new(ctypes.TMResult) - _, err := c.rpc.Call("net_info", map[string]interface{}{}, tmResult) + result := new(ctypes.ResultNetInfo) + _, err := c.rpc.Call("net_info", map[string]interface{}{}, result) if err != nil { return nil, errors.Wrap(err, "NetInfo") } - return tmResult.Unwrap().(*ctypes.ResultNetInfo), nil + return result, nil } func (c *HTTP) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { - tmResult := new(ctypes.TMResult) - _, err := c.rpc.Call("dump_consensus_state", map[string]interface{}{}, tmResult) + result := new(ctypes.ResultDumpConsensusState) + _, err := c.rpc.Call("dump_consensus_state", map[string]interface{}{}, result) if err != nil { return nil, errors.Wrap(err, "DumpConsensusState") } - return tmResult.Unwrap().(*ctypes.ResultDumpConsensusState), nil + return result, nil } func (c *HTTP) BlockchainInfo(minHeight, maxHeight int) (*ctypes.ResultBlockchainInfo, error) { - tmResult := new(ctypes.TMResult) + result := new(ctypes.ResultBlockchainInfo) _, err := c.rpc.Call("blockchain", map[string]interface{}{"minHeight": minHeight, "maxHeight": maxHeight}, - tmResult) + result) if err != nil { return nil, errors.Wrap(err, "BlockchainInfo") } - return tmResult.Unwrap().(*ctypes.ResultBlockchainInfo), nil + return result, nil } func (c *HTTP) Genesis() (*ctypes.ResultGenesis, error) { - tmResult := new(ctypes.TMResult) - _, err := c.rpc.Call("genesis", map[string]interface{}{}, tmResult) + result := new(ctypes.ResultGenesis) + _, err := c.rpc.Call("genesis", map[string]interface{}{}, result) if err != nil { return nil, errors.Wrap(err, "Genesis") } - return tmResult.Unwrap().(*ctypes.ResultGenesis), nil + return result, nil } func (c *HTTP) Block(height int) (*ctypes.ResultBlock, error) { - tmResult := new(ctypes.TMResult) - _, err := c.rpc.Call("block", map[string]interface{}{"height": height}, tmResult) + result := new(ctypes.ResultBlock) + _, err := c.rpc.Call("block", map[string]interface{}{"height": height}, result) if err != nil { return nil, errors.Wrap(err, "Block") } - return tmResult.Unwrap().(*ctypes.ResultBlock), nil + return result, nil } func (c *HTTP) Commit(height int) (*ctypes.ResultCommit, error) { - tmResult := new(ctypes.TMResult) - _, err := c.rpc.Call("commit", map[string]interface{}{"height": height}, tmResult) + result := new(ctypes.ResultCommit) + _, err := c.rpc.Call("commit", map[string]interface{}{"height": height}, result) if err != nil { return nil, errors.Wrap(err, "Commit") } - return tmResult.Unwrap().(*ctypes.ResultCommit), nil + return result, nil } func (c *HTTP) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) { - tmResult := new(ctypes.TMResult) + result := new(ctypes.ResultTx) query := map[string]interface{}{ "hash": hash, "prove": prove, } - _, err := c.rpc.Call("tx", query, tmResult) + _, err := c.rpc.Call("tx", query, result) if err != nil { return nil, errors.Wrap(err, "Tx") } - return tmResult.Unwrap().(*ctypes.ResultTx), nil + return result, nil } func (c *HTTP) Validators() (*ctypes.ResultValidators, error) { - tmResult := new(ctypes.TMResult) - _, err := c.rpc.Call("validators", map[string]interface{}{}, tmResult) + result := new(ctypes.ResultValidators) + _, err := c.rpc.Call("validators", map[string]interface{}{}, result) if err != nil { return nil, errors.Wrap(err, "Validators") } - return tmResult.Unwrap().(*ctypes.ResultValidators), nil + return result, nil } /** websocket event stuff here... **/ @@ -335,18 +334,15 @@ func (w *WSEvents) eventListener() { // some implementation of types.TMEventData, and sends it off // on the merry way to the EventSwitch func (w *WSEvents) parseEvent(data []byte) (err error) { - result := new(ctypes.TMResult) + result := new(ctypes.ResultEvent) err = json.Unmarshal(data, result) if err != nil { - return err - } - event, ok := result.Unwrap().(*ctypes.ResultEvent) - if !ok { // ignore silently (eg. subscribe, unsubscribe and maybe other events) + // TODO: ? return nil } // looks good! let's fire this baby! - w.EventSwitch.FireEvent(event.Name, event.Data) + w.EventSwitch.FireEvent(result.Name, result.Data) return nil } diff --git a/rpc/core/events.go b/rpc/core/events.go index 271ba5301..fa40c5a81 100644 --- a/rpc/core/events.go +++ b/rpc/core/events.go @@ -11,8 +11,8 @@ func Subscribe(wsCtx rpctypes.WSRPCContext, event string) (*ctypes.ResultSubscri types.AddListenerForEvent(wsCtx.GetEventSwitch(), wsCtx.GetRemoteAddr(), event, func(msg types.TMEventData) { // NOTE: EventSwitch callbacks must be nonblocking // NOTE: RPCResponses of subscribed events have id suffix "#event" - tmResult := ctypes.TMResult{&ctypes.ResultEvent{event, msg}} - wsCtx.TryWriteRPCResponse(rpctypes.NewRPCResponse(wsCtx.Request.ID+"#event", &tmResult, "")) + tmResult := &ctypes.ResultEvent{event, msg} + wsCtx.TryWriteRPCResponse(rpctypes.NewRPCResponse(wsCtx.Request.ID+"#event", tmResult, "")) }) return &ctypes.ResultSubscribe{}, nil } diff --git a/rpc/core/routes.go b/rpc/core/routes.go index 9e4c03be1..b662ae48c 100644 --- a/rpc/core/routes.go +++ b/rpc/core/routes.go @@ -1,173 +1,46 @@ package core import ( - data "github.com/tendermint/go-wire/data" - ctypes "github.com/tendermint/tendermint/rpc/core/types" rpc "github.com/tendermint/tendermint/rpc/lib/server" - "github.com/tendermint/tendermint/rpc/lib/types" - "github.com/tendermint/tendermint/types" ) // TODO: better system than "unsafe" prefix var Routes = map[string]*rpc.RPCFunc{ // subscribe/unsubscribe are reserved for websocket events. - "subscribe": rpc.NewWSRPCFunc(SubscribeResult, "event"), - "unsubscribe": rpc.NewWSRPCFunc(UnsubscribeResult, "event"), + "subscribe": rpc.NewWSRPCFunc(Subscribe, "event"), + "unsubscribe": rpc.NewWSRPCFunc(Unsubscribe, "event"), // info API - "status": rpc.NewRPCFunc(StatusResult, ""), - "net_info": rpc.NewRPCFunc(NetInfoResult, ""), - "blockchain": rpc.NewRPCFunc(BlockchainInfoResult, "minHeight,maxHeight"), - "genesis": rpc.NewRPCFunc(GenesisResult, ""), - "block": rpc.NewRPCFunc(BlockResult, "height"), - "commit": rpc.NewRPCFunc(CommitResult, "height"), - "tx": rpc.NewRPCFunc(TxResult, "hash,prove"), - "validators": rpc.NewRPCFunc(ValidatorsResult, ""), - "dump_consensus_state": rpc.NewRPCFunc(DumpConsensusStateResult, ""), - "unconfirmed_txs": rpc.NewRPCFunc(UnconfirmedTxsResult, ""), - "num_unconfirmed_txs": rpc.NewRPCFunc(NumUnconfirmedTxsResult, ""), + "status": rpc.NewRPCFunc(Status, ""), + "net_info": rpc.NewRPCFunc(NetInfo, ""), + "blockchain": rpc.NewRPCFunc(BlockchainInfo, "minHeight,maxHeight"), + "genesis": rpc.NewRPCFunc(Genesis, ""), + "block": rpc.NewRPCFunc(Block, "height"), + "commit": rpc.NewRPCFunc(Commit, "height"), + "tx": rpc.NewRPCFunc(Tx, "hash,prove"), + "validators": rpc.NewRPCFunc(Validators, ""), + "dump_consensus_state": rpc.NewRPCFunc(DumpConsensusState, ""), + "unconfirmed_txs": rpc.NewRPCFunc(UnconfirmedTxs, ""), + "num_unconfirmed_txs": rpc.NewRPCFunc(NumUnconfirmedTxs, ""), // broadcast API - "broadcast_tx_commit": rpc.NewRPCFunc(BroadcastTxCommitResult, "tx"), - "broadcast_tx_sync": rpc.NewRPCFunc(BroadcastTxSyncResult, "tx"), - "broadcast_tx_async": rpc.NewRPCFunc(BroadcastTxAsyncResult, "tx"), + "broadcast_tx_commit": rpc.NewRPCFunc(BroadcastTxCommit, "tx"), + "broadcast_tx_sync": rpc.NewRPCFunc(BroadcastTxSync, "tx"), + "broadcast_tx_async": rpc.NewRPCFunc(BroadcastTxAsync, "tx"), // abci API - "abci_query": rpc.NewRPCFunc(ABCIQueryResult, "path,data,prove"), - "abci_info": rpc.NewRPCFunc(ABCIInfoResult, ""), + "abci_query": rpc.NewRPCFunc(ABCIQuery, "path,data,prove"), + "abci_info": rpc.NewRPCFunc(ABCIInfo, ""), // control API - "dial_seeds": rpc.NewRPCFunc(UnsafeDialSeedsResult, "seeds"), + "dial_seeds": rpc.NewRPCFunc(UnsafeDialSeeds, "seeds"), "unsafe_flush_mempool": rpc.NewRPCFunc(UnsafeFlushMempool, ""), // config is not in general thread safe. expose specifics if you need em - // "unsafe_set_config": rpc.NewRPCFunc(UnsafeSetConfigResult, "type,key,value"), + // "unsafe_set_config": rpc.NewRPCFunc(UnsafeSetConfig, "type,key,value"), // profiler API - "unsafe_start_cpu_profiler": rpc.NewRPCFunc(UnsafeStartCPUProfilerResult, "filename"), - "unsafe_stop_cpu_profiler": rpc.NewRPCFunc(UnsafeStopCPUProfilerResult, ""), - "unsafe_write_heap_profile": rpc.NewRPCFunc(UnsafeWriteHeapProfileResult, "filename"), -} - -func SubscribeResult(wsCtx rpctypes.WSRPCContext, event string) (ctypes.TMResult, error) { - res, err := Subscribe(wsCtx, event) - return ctypes.TMResult{res}, err -} - -func UnsubscribeResult(wsCtx rpctypes.WSRPCContext, event string) (ctypes.TMResult, error) { - res, err := Unsubscribe(wsCtx, event) - return ctypes.TMResult{res}, err -} - -func StatusResult() (ctypes.TMResult, error) { - res, err := Status() - return ctypes.TMResult{res}, err -} - -func NetInfoResult() (ctypes.TMResult, error) { - res, err := NetInfo() - return ctypes.TMResult{res}, err -} - -func UnsafeDialSeedsResult(seeds []string) (ctypes.TMResult, error) { - res, err := UnsafeDialSeeds(seeds) - return ctypes.TMResult{res}, err -} - -func BlockchainInfoResult(min, max int) (ctypes.TMResult, error) { - res, err := BlockchainInfo(min, max) - return ctypes.TMResult{res}, err -} - -func GenesisResult() (ctypes.TMResult, error) { - res, err := Genesis() - return ctypes.TMResult{res}, err -} - -func BlockResult(height int) (ctypes.TMResult, error) { - res, err := Block(height) - return ctypes.TMResult{res}, err -} - -func CommitResult(height int) (ctypes.TMResult, error) { - res, err := Commit(height) - return ctypes.TMResult{res}, err -} - -func ValidatorsResult() (ctypes.TMResult, error) { - res, err := Validators() - return ctypes.TMResult{res}, err -} - -func DumpConsensusStateResult() (ctypes.TMResult, error) { - res, err := DumpConsensusState() - return ctypes.TMResult{res}, err -} - -func UnconfirmedTxsResult() (ctypes.TMResult, error) { - res, err := UnconfirmedTxs() - return ctypes.TMResult{res}, err -} - -func NumUnconfirmedTxsResult() (ctypes.TMResult, error) { - res, err := NumUnconfirmedTxs() - return ctypes.TMResult{res}, err -} - -// 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, prove bool) (ctypes.TMResult, error) { - res, err := Tx(hash, prove) - return ctypes.TMResult{res}, err -} - -func BroadcastTxCommitResult(tx types.Tx) (ctypes.TMResult, error) { - res, err := BroadcastTxCommit(tx) - return ctypes.TMResult{res}, err -} - -func BroadcastTxSyncResult(tx types.Tx) (ctypes.TMResult, error) { - res, err := BroadcastTxSync(tx) - return ctypes.TMResult{res}, err -} - -func BroadcastTxAsyncResult(tx types.Tx) (ctypes.TMResult, error) { - res, err := BroadcastTxAsync(tx) - return ctypes.TMResult{res}, err -} - -func ABCIQueryResult(path string, data data.Bytes, prove bool) (ctypes.TMResult, error) { - res, err := ABCIQuery(path, data, prove) - return ctypes.TMResult{res}, err -} - -func ABCIInfoResult() (ctypes.TMResult, error) { - res, err := ABCIInfo() - return ctypes.TMResult{res}, err -} - -func UnsafeFlushMempoolResult() (ctypes.TMResult, error) { - res, err := UnsafeFlushMempool() - return ctypes.TMResult{res}, err -} - -func UnsafeSetConfigResult(typ, key, value string) (ctypes.TMResult, error) { - res, err := UnsafeSetConfig(typ, key, value) - return ctypes.TMResult{res}, err -} - -func UnsafeStartCPUProfilerResult(filename string) (ctypes.TMResult, error) { - res, err := UnsafeStartCPUProfiler(filename) - return ctypes.TMResult{res}, err -} - -func UnsafeStopCPUProfilerResult() (ctypes.TMResult, error) { - res, err := UnsafeStopCPUProfiler() - return ctypes.TMResult{res}, err -} - -func UnsafeWriteHeapProfileResult(filename string) (ctypes.TMResult, error) { - res, err := UnsafeWriteHeapProfile(filename) - return ctypes.TMResult{res}, err + "unsafe_start_cpu_profiler": rpc.NewRPCFunc(UnsafeStartCPUProfiler, "filename"), + "unsafe_stop_cpu_profiler": rpc.NewRPCFunc(UnsafeStopCPUProfiler, ""), + "unsafe_write_heap_profile": rpc.NewRPCFunc(UnsafeWriteHeapProfile, "filename"), } diff --git a/rpc/core/tx.go b/rpc/core/tx.go index 387d4afa6..5bd6e1806 100644 --- a/rpc/core/tx.go +++ b/rpc/core/tx.go @@ -8,6 +8,9 @@ import ( "github.com/tendermint/tendermint/types" ) +// 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 Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) { // if index is disabled, return error diff --git a/rpc/core/types/responses.go b/rpc/core/types/responses.go index 9b4e9d579..23d68587b 100644 --- a/rpc/core/types/responses.go +++ b/rpc/core/types/responses.go @@ -7,7 +7,7 @@ import ( "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire/data" "github.com/tendermint/tendermint/p2p" - "github.com/tendermint/tendermint/rpc/lib/types" + // "github.com/tendermint/tendermint/rpc/lib/types" "github.com/tendermint/tendermint/types" ) @@ -212,60 +212,3 @@ const ( ResultNameUnsafeWriteHeapProfile = "unsafe_write_heap" ResultNameUnsafeFlushMempool = "unsafe_flush_mempool" ) - -type TMResultInner interface { - rpctypes.Result -} - -type TMResult struct { - TMResultInner `json:"unwrap"` -} - -func (tmr TMResult) MarshalJSON() ([]byte, error) { - return tmResultMapper.ToJSON(tmr.TMResultInner) -} - -func (tmr *TMResult) UnmarshalJSON(data []byte) (err error) { - parsed, err := tmResultMapper.FromJSON(data) - if err == nil && parsed != nil { - tmr.TMResultInner = parsed.(TMResultInner) - } - return -} - -func (tmr TMResult) Unwrap() TMResultInner { - tmrI := tmr.TMResultInner - for wrap, ok := tmrI.(TMResult); ok; wrap, ok = tmrI.(TMResult) { - tmrI = wrap.TMResultInner - } - return tmrI -} - -func (tmr TMResult) Empty() bool { - return tmr.TMResultInner == nil -} - -var tmResultMapper = data.NewMapper(TMResult{}). - RegisterImplementation(&ResultGenesis{}, ResultNameGenesis, ResultTypeGenesis). - RegisterImplementation(&ResultBlockchainInfo{}, ResultNameBlockchainInfo, ResultTypeBlockchainInfo). - RegisterImplementation(&ResultBlock{}, ResultNameBlock, ResultTypeBlock). - RegisterImplementation(&ResultCommit{}, ResultNameCommit, ResultTypeCommit). - RegisterImplementation(&ResultStatus{}, ResultNameStatus, ResultTypeStatus). - RegisterImplementation(&ResultNetInfo{}, ResultNameNetInfo, ResultTypeNetInfo). - RegisterImplementation(&ResultDialSeeds{}, ResultNameDialSeeds, ResultTypeDialSeeds). - RegisterImplementation(&ResultValidators{}, ResultNameValidators, ResultTypeValidators). - RegisterImplementation(&ResultDumpConsensusState{}, ResultNameDumpConsensusState, ResultTypeDumpConsensusState). - RegisterImplementation(&ResultBroadcastTx{}, ResultNameBroadcastTx, ResultTypeBroadcastTx). - RegisterImplementation(&ResultBroadcastTxCommit{}, ResultNameBroadcastTxCommit, ResultTypeBroadcastTxCommit). - RegisterImplementation(&ResultTx{}, ResultNameTx, ResultTypeTx). - RegisterImplementation(&ResultUnconfirmedTxs{}, ResultNameUnconfirmedTxs, ResultTypeUnconfirmedTxs). - RegisterImplementation(&ResultSubscribe{}, ResultNameSubscribe, ResultTypeSubscribe). - RegisterImplementation(&ResultUnsubscribe{}, ResultNameUnsubscribe, ResultTypeUnsubscribe). - RegisterImplementation(&ResultEvent{}, ResultNameEvent, ResultTypeEvent). - RegisterImplementation(&ResultUnsafeSetConfig{}, ResultNameUnsafeSetConfig, ResultTypeUnsafeSetConfig). - RegisterImplementation(&ResultUnsafeProfile{}, ResultNameUnsafeStartCPUProfiler, ResultTypeUnsafeStartCPUProfiler). - RegisterImplementation(&ResultUnsafeProfile{}, ResultNameUnsafeStopCPUProfiler, ResultTypeUnsafeStopCPUProfiler). - RegisterImplementation(&ResultUnsafeProfile{}, ResultNameUnsafeWriteHeapProfile, ResultTypeUnsafeWriteHeapProfile). - RegisterImplementation(&ResultUnsafeFlushMempool{}, ResultNameUnsafeFlushMempool, ResultTypeUnsafeFlushMempool). - RegisterImplementation(&ResultABCIQuery{}, ResultNameABCIQuery, ResultTypeABCIQuery). - RegisterImplementation(&ResultABCIInfo{}, ResultNameABCIInfo, ResultTypeABCIInfo) diff --git a/rpc/test/client_test.go b/rpc/test/client_test.go index 8e636c333..aed179d47 100644 --- a/rpc/test/client_test.go +++ b/rpc/test/client_test.go @@ -39,12 +39,11 @@ func TestJSONStatus(t *testing.T) { func testStatus(t *testing.T, client rpc.HTTPClient) { chainID := GetConfig().GetString("chain_id") - tmResult := new(ctypes.TMResult) - _, err := client.Call("status", map[string]interface{}{}, tmResult) + result := new(ctypes.ResultStatus) + _, err := client.Call("status", map[string]interface{}{}, result) require.Nil(t, err) - status := tmResult.Unwrap().(*ctypes.ResultStatus) - assert.Equal(t, chainID, status.NodeInfo.Network) + assert.Equal(t, chainID, result.NodeInfo.Network) } //-------------------------------------------------------------------------------- @@ -70,13 +69,12 @@ func TestJSONBroadcastTxSync(t *testing.T) { func testBroadcastTxSync(t *testing.T, client rpc.HTTPClient) { mem := node.MempoolReactor().Mempool initMemSize := mem.Size() - tmResult := new(ctypes.TMResult) + result := new(ctypes.ResultBroadcastTx) tx := randBytes(t) - _, err := client.Call("broadcast_tx_sync", map[string]interface{}{"tx": tx}, tmResult) + _, err := client.Call("broadcast_tx_sync", map[string]interface{}{"tx": tx}, result) require.Nil(t, err) - res := tmResult.Unwrap().(*ctypes.ResultBroadcastTx) - require.Equal(t, abci.CodeType_OK, res.Code) + require.Equal(t, abci.CodeType_OK, result.Code) require.Equal(t, initMemSize+1, mem.Size()) txs := mem.Reap(1) require.EqualValues(t, tx, txs[0]) @@ -93,14 +91,13 @@ func testTxKV(t *testing.T) ([]byte, []byte, types.Tx) { } func sendTx(t *testing.T, client rpc.HTTPClient) ([]byte, []byte) { - tmResult := new(ctypes.TMResult) + result := new(ctypes.ResultBroadcastTxCommit) k, v, tx := testTxKV(t) - _, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, tmResult) + _, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, result) require.Nil(t, err) - bres := tmResult.Unwrap().(*ctypes.ResultBroadcastTxCommit) - require.NotNil(t, 0, bres.DeliverTx, "%#v", bres) - require.EqualValues(t, 0, bres.CheckTx.Code, "%#v", bres) - require.EqualValues(t, 0, bres.DeliverTx.Code, "%#v", bres) + require.NotNil(t, 0, result.DeliverTx, "%#v", result) + require.EqualValues(t, 0, result.CheckTx.Code, "%#v", result) + require.EqualValues(t, 0, result.DeliverTx.Code, "%#v", result) return k, v } @@ -115,16 +112,15 @@ func TestJSONABCIQuery(t *testing.T) { func testABCIQuery(t *testing.T, client rpc.HTTPClient) { k, _ := sendTx(t, client) time.Sleep(time.Millisecond * 500) - tmResult := new(ctypes.TMResult) + result := new(ctypes.ResultABCIQuery) _, err := client.Call("abci_query", - map[string]interface{}{"path": "", "data": data.Bytes(k), "prove": false}, tmResult) + map[string]interface{}{"path": "", "data": data.Bytes(k), "prove": false}, result) require.Nil(t, err) - resQuery := tmResult.Unwrap().(*ctypes.ResultABCIQuery) - require.EqualValues(t, 0, resQuery.Code) + require.EqualValues(t, 0, result.Code) // XXX: specific to value returned by the dummy - require.NotEqual(t, 0, len(resQuery.Value)) + require.NotEqual(t, 0, len(result.Value)) } //-------------------------------------------------------------------------------- @@ -141,15 +137,14 @@ func TestJSONBroadcastTxCommit(t *testing.T) { func testBroadcastTxCommit(t *testing.T, client rpc.HTTPClient) { require := require.New(t) - tmResult := new(ctypes.TMResult) + result := new(ctypes.ResultBroadcastTxCommit) tx := randBytes(t) - _, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, tmResult) + _, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, result) require.Nil(err) - res := tmResult.Unwrap().(*ctypes.ResultBroadcastTxCommit) - checkTx := res.CheckTx + checkTx := result.CheckTx require.Equal(abci.CodeType_OK, checkTx.Code) - deliverTx := res.DeliverTx + deliverTx := result.DeliverTx require.Equal(abci.CodeType_OK, deliverTx.Code) mem := node.MempoolReactor().Mempool require.Equal(0, mem.Size()) @@ -179,16 +174,15 @@ func testTx(t *testing.T, client rpc.HTTPClient, withIndexer bool) { assert, require := assert.New(t), require.New(t) // first we broadcast a tx - tmResult := new(ctypes.TMResult) + result := new(ctypes.ResultBroadcastTxCommit) txBytes := randBytes(t) tx := types.Tx(txBytes) - _, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": txBytes}, tmResult) + _, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": txBytes}, result) require.Nil(err) - res := tmResult.Unwrap().(*ctypes.ResultBroadcastTxCommit) - checkTx := res.CheckTx + checkTx := result.CheckTx require.Equal(abci.CodeType_OK, checkTx.Code) - deliverTx := res.DeliverTx + deliverTx := result.DeliverTx require.Equal(abci.CodeType_OK, deliverTx.Code) mem := node.MempoolReactor().Mempool require.Equal(0, mem.Size()) @@ -215,24 +209,23 @@ func testTx(t *testing.T, client rpc.HTTPClient, withIndexer bool) { // now we query for the tx. // since there's only one tx, we know index=0. - tmResult = new(ctypes.TMResult) + result2 := new(ctypes.ResultTx) query := map[string]interface{}{ "hash": tc.hash, "prove": tc.prove, } - _, err = client.Call("tx", query, tmResult) + _, err = client.Call("tx", query, result2) valid := (withIndexer && tc.valid) if !valid { require.NotNil(err, idx) } else { require.Nil(err, idx) - res2 := tmResult.Unwrap().(*ctypes.ResultTx) - assert.Equal(tx, res2.Tx, idx) - assert.Equal(res.Height, res2.Height, idx) - assert.Equal(0, res2.Index, idx) - assert.Equal(abci.CodeType_OK, res2.TxResult.Code, idx) + assert.Equal(tx, result2.Tx, idx) + assert.Equal(result.Height, result2.Height, idx) + assert.Equal(0, result2.Index, idx) + assert.Equal(abci.CodeType_OK, result2.TxResult.Code, idx) // time to verify the proof - proof := res2.Proof + proof := result2.Proof if tc.prove && assert.Equal(tx, proof.Data, idx) { assert.True(proof.Proof.Verify(proof.Index, proof.Total, tx.Hash(), proof.RootHash), idx) } @@ -316,8 +309,8 @@ func TestWSTxEvent(t *testing.T) { }() // send an tx - tmResult := new(ctypes.TMResult) - _, err := GetJSONClient().Call("broadcast_tx_sync", map[string]interface{}{"tx": tx}, tmResult) + result := new(ctypes.ResultBroadcastTx) + _, err := GetJSONClient().Call("broadcast_tx_sync", map[string]interface{}{"tx": tx}, result) require.Nil(err) waitForEvent(t, wsc, eid, true, func() {}, func(eid string, b interface{}) error { @@ -374,12 +367,12 @@ func TestWSDoubleFire(t *testing.T) { // //func TestURIUnsafeSetConfig(t *testing.T) { // for _, testCase := range testCasesUnsafeSetConfig { -// tmResult := new(ctypes.TMResult) +// result := new(ctypes.TMResult) // _, err := GetURIClient().Call("unsafe_set_config", map[string]interface{}{ // "type": testCase[0], // "key": testCase[1], // "value": testCase[2], -// }, tmResult) +// }, result) // require.Nil(t, err) // } // testUnsafeSetConfig(t) @@ -387,10 +380,10 @@ func TestWSDoubleFire(t *testing.T) { // //func TestJSONUnsafeSetConfig(t *testing.T) { // for _, testCase := range testCasesUnsafeSetConfig { -// tmResult := new(ctypes.TMResult) +// result := new(ctypes.TMResult) // _, err := GetJSONClient().Call("unsafe_set_config", // map[string]interface{}{"type": testCase[0], "key": testCase[1], "value": testCase[2]}, -// tmResult) +// result) // require.Nil(t, err) // } // testUnsafeSetConfig(t) diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index 2b01d0a69..a12d82f23 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -132,16 +132,15 @@ func waitForEvent(t *testing.T, wsc *client.WSClient, eventid string, dieOnTimeo select { case r := <-wsc.ResultsCh: fmt.Println("GOT IT", string(r)) - result := new(ctypes.TMResult) + result := new(ctypes.ResultEvent) err = json.Unmarshal(r, result) if err != nil { fmt.Println("POOP", err) - errCh <- err - break LOOP + // cant distinguish between error and wrong type ... + continue } - event, ok := result.Unwrap().(*ctypes.ResultEvent) - if ok && event.Name == eventid { - goodCh <- event.Data + if result.Name == eventid { + goodCh <- result.Data break LOOP } case err := <-wsc.ErrorsCh: From aa9e673ed78ae6d8bae5b141e9299753f9382fc1 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 28 Apr 2017 22:31:30 -0400 Subject: [PATCH 16/23] test: jq .result[1] -> jq .result --- test/app/counter_test.sh | 2 +- test/app/dummy_test.sh | 4 ++-- test/p2p/atomic_broadcast/test.sh | 10 +++++----- test/p2p/basic/test.sh | 8 ++++---- test/p2p/fast_sync/check_peer.sh | 8 ++++---- test/p2p/kill_all/check_peers.sh | 6 +++--- test/p2p/pex/check_peer.sh | 2 +- test/persist/test_failure_indices.sh | 4 ++-- test/persist/test_simple.sh | 4 ++-- 9 files changed, 24 insertions(+), 24 deletions(-) diff --git a/test/app/counter_test.sh b/test/app/counter_test.sh index 439926a5d..cc5c38b25 100644 --- a/test/app/counter_test.sh +++ b/test/app/counter_test.sh @@ -33,7 +33,7 @@ function sendTx() { ERROR=`echo $RESPONSE | jq .error` ERROR=$(echo "$ERROR" | tr -d '"') # remove surrounding quotes - RESPONSE=`echo $RESPONSE | jq .result[1]` + RESPONSE=`echo $RESPONSE | jq .result` else if [ -f grpc_client ]; then rm grpc_client diff --git a/test/app/dummy_test.sh b/test/app/dummy_test.sh index 0449bc491..1a117c634 100644 --- a/test/app/dummy_test.sh +++ b/test/app/dummy_test.sh @@ -57,7 +57,7 @@ echo "... testing query with /abci_query 2" # we should be able to look up the key RESPONSE=`curl -s "127.0.0.1:46657/abci_query?path=\"\"&data=$(toHex $KEY)&prove=false"` -RESPONSE=`echo $RESPONSE | jq .result[1].response.log` +RESPONSE=`echo $RESPONSE | jq .result.response.log` set +e A=`echo $RESPONSE | grep 'exists'` @@ -70,7 +70,7 @@ set -e # we should not be able to look up the value RESPONSE=`curl -s "127.0.0.1:46657/abci_query?path=\"\"&data=$(toHex $VALUE)&prove=false"` -RESPONSE=`echo $RESPONSE | jq .result[1].response.log` +RESPONSE=`echo $RESPONSE | jq .result.response.log` set +e A=`echo $RESPONSE | grep 'exists'` if [[ $? == 0 ]]; then diff --git a/test/p2p/atomic_broadcast/test.sh b/test/p2p/atomic_broadcast/test.sh index 8e0633c8a..00b339631 100644 --- a/test/p2p/atomic_broadcast/test.sh +++ b/test/p2p/atomic_broadcast/test.sh @@ -17,7 +17,7 @@ for i in `seq 1 $N`; do addr=$(test/p2p/ip.sh $i):46657 # current state - HASH1=`curl -s $addr/status | jq .result[1].latest_app_hash` + HASH1=`curl -s $addr/status | jq .result.latest_app_hash` # - send a tx TX=aadeadbeefbeefbeef0$i @@ -26,15 +26,15 @@ for i in `seq 1 $N`; do echo "" # we need to wait another block to get the new app_hash - h1=`curl -s $addr/status | jq .result[1].latest_block_height` + h1=`curl -s $addr/status | jq .result.latest_block_height` h2=$h1 while [ "$h2" == "$h1" ]; do sleep 1 - h2=`curl -s $addr/status | jq .result[1].latest_block_height` + h2=`curl -s $addr/status | jq .result.latest_block_height` done # check that hash was updated - HASH2=`curl -s $addr/status | jq .result[1].latest_app_hash` + HASH2=`curl -s $addr/status | jq .result.latest_app_hash` if [[ "$HASH1" == "$HASH2" ]]; then echo "Expected state hash to update from $HASH1. Got $HASH2" exit 1 @@ -44,7 +44,7 @@ for i in `seq 1 $N`; do for j in `seq 1 $N`; do if [[ "$i" != "$j" ]]; then addrJ=$(test/p2p/ip.sh $j):46657 - HASH3=`curl -s $addrJ/status | jq .result[1].latest_app_hash` + HASH3=`curl -s $addrJ/status | jq .result.latest_app_hash` if [[ "$HASH2" != "$HASH3" ]]; then echo "App hash for node $j doesn't match. Got $HASH3, expected $HASH2" diff --git a/test/p2p/basic/test.sh b/test/p2p/basic/test.sh index 3399515a8..93444792b 100644 --- a/test/p2p/basic/test.sh +++ b/test/p2p/basic/test.sh @@ -31,19 +31,19 @@ for i in `seq 1 $N`; do N_1=$(($N - 1)) # - assert everyone has N-1 other peers - N_PEERS=`curl -s $addr/net_info | jq '.result[1].peers | length'` + N_PEERS=`curl -s $addr/net_info | jq '.result.peers | length'` while [ "$N_PEERS" != $N_1 ]; do echo "Waiting for node $i to connect to all peers ..." sleep 1 - N_PEERS=`curl -s $addr/net_info | jq '.result[1].peers | length'` + N_PEERS=`curl -s $addr/net_info | jq '.result.peers | length'` done # - assert block height is greater than 1 - BLOCK_HEIGHT=`curl -s $addr/status | jq .result[1].latest_block_height` + BLOCK_HEIGHT=`curl -s $addr/status | jq .result.latest_block_height` while [ "$BLOCK_HEIGHT" -le 1 ]; do echo "Waiting for node $i to commit a block ..." sleep 1 - BLOCK_HEIGHT=`curl -s $addr/status | jq .result[1].latest_block_height` + BLOCK_HEIGHT=`curl -s $addr/status | jq .result.latest_block_height` done echo "Node $i is connected to all peers and at block $BLOCK_HEIGHT" done diff --git a/test/p2p/fast_sync/check_peer.sh b/test/p2p/fast_sync/check_peer.sh index c459277d2..b10c3efc5 100644 --- a/test/p2p/fast_sync/check_peer.sh +++ b/test/p2p/fast_sync/check_peer.sh @@ -15,10 +15,10 @@ peerID=$(( $(($ID % 4)) + 1 )) # 1->2 ... 3->4 ... 4->1 peer_addr=$(test/p2p/ip.sh $peerID):46657 # get another peer's height -h1=`curl -s $peer_addr/status | jq .result[1].latest_block_height` +h1=`curl -s $peer_addr/status | jq .result.latest_block_height` # get another peer's state -root1=`curl -s $peer_addr/status | jq .result[1].latest_app_hash` +root1=`curl -s $peer_addr/status | jq .result.latest_app_hash` echo "Other peer is on height $h1 with state $root1" echo "Waiting for peer $ID to catch up" @@ -29,12 +29,12 @@ set +o pipefail h2="0" while [[ "$h2" -lt "$(($h1+3))" ]]; do sleep 1 - h2=`curl -s $addr/status | jq .result[1].latest_block_height` + h2=`curl -s $addr/status | jq .result.latest_block_height` echo "... $h2" done # check the app hash -root2=`curl -s $addr/status | jq .result[1].latest_app_hash` +root2=`curl -s $addr/status | jq .result.latest_app_hash` if [[ "$root1" != "$root2" ]]; then echo "App hash after fast sync does not match. Got $root2; expected $root1" diff --git a/test/p2p/kill_all/check_peers.sh b/test/p2p/kill_all/check_peers.sh index d085a025c..52dcde91c 100644 --- a/test/p2p/kill_all/check_peers.sh +++ b/test/p2p/kill_all/check_peers.sh @@ -23,7 +23,7 @@ set -e # get the first peer's height addr=$(test/p2p/ip.sh 1):46657 -h1=$(curl -s "$addr/status" | jq .result[1].latest_block_height) +h1=$(curl -s "$addr/status" | jq .result.latest_block_height) echo "1st peer is on height $h1" echo "Waiting until other peers reporting a height higher than the 1st one" @@ -33,14 +33,14 @@ for i in $(seq 2 "$NUM_OF_PEERS"); do while [[ $hi -le $h1 ]] ; do addr=$(test/p2p/ip.sh "$i"):46657 - hi=$(curl -s "$addr/status" | jq .result[1].latest_block_height) + hi=$(curl -s "$addr/status" | jq .result.latest_block_height) echo "... peer $i is on height $hi" ((attempt++)) if [ "$attempt" -ge $MAX_ATTEMPTS_TO_CATCH_UP ] ; then echo "$attempt unsuccessful attempts were made to catch up" - curl -s "$addr/dump_consensus_state" | jq .result[1] + curl -s "$addr/dump_consensus_state" | jq .result exit 1 fi diff --git a/test/p2p/pex/check_peer.sh b/test/p2p/pex/check_peer.sh index ceabd2ac6..e851c8923 100644 --- a/test/p2p/pex/check_peer.sh +++ b/test/p2p/pex/check_peer.sh @@ -10,7 +10,7 @@ echo "2. wait until peer $ID connects to other nodes using pex reactor" peers_count="0" while [[ "$peers_count" -lt "$((N-1))" ]]; do sleep 1 - peers_count=$(curl -s "$addr/net_info" | jq ".result[1].peers | length") + peers_count=$(curl -s "$addr/net_info" | jq ".result.peers | length") echo "... peers count = $peers_count, expected = $((N-1))" done diff --git a/test/persist/test_failure_indices.sh b/test/persist/test_failure_indices.sh index bca8b8ae9..9e3c8f19e 100644 --- a/test/persist/test_failure_indices.sh +++ b/test/persist/test_failure_indices.sh @@ -107,11 +107,11 @@ for failIndex in $(seq $failsStart $failsEnd); do done # wait for a new block - h1=$(curl -s --unix-socket "$RPC_ADDR" http://localhost/status | jq .result[1].latest_block_height) + h1=$(curl -s --unix-socket "$RPC_ADDR" http://localhost/status | jq .result.latest_block_height) h2=$h1 while [ "$h2" == "$h1" ]; do sleep 1 - h2=$(curl -s --unix-socket "$RPC_ADDR" http://localhost/status | jq .result[1].latest_block_height) + h2=$(curl -s --unix-socket "$RPC_ADDR" http://localhost/status | jq .result.latest_block_height) done kill_procs diff --git a/test/persist/test_simple.sh b/test/persist/test_simple.sh index 59bc38458..273c714ca 100644 --- a/test/persist/test_simple.sh +++ b/test/persist/test_simple.sh @@ -57,11 +57,11 @@ while [ "$ERR" != 0 ]; do done # wait for a new block -h1=`curl -s $addr/status | jq .result[1].latest_block_height` +h1=`curl -s $addr/status | jq .result.latest_block_height` h2=$h1 while [ "$h2" == "$h1" ]; do sleep 1 - h2=`curl -s $addr/status | jq .result[1].latest_block_height` + h2=`curl -s $addr/status | jq .result.latest_block_height` done kill_procs From efeadcc0f44f563ebab9870f446c89cb39509208 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 28 Apr 2017 23:18:38 -0400 Subject: [PATCH 17/23] some cleanup from review --- cmd/tendermint/commands/gen_validator.go | 4 +- rpc/core/types/responses.go | 88 +----------------------- rpc/lib/README.md | 12 +--- rpc/lib/client/http_client.go | 7 -- rpc/lib/client/ws_client.go | 7 -- rpc/lib/server/http_server.go | 1 - rpc/lib/types/types.go | 16 ----- rpc/test/helpers.go | 2 - 8 files changed, 6 insertions(+), 131 deletions(-) diff --git a/cmd/tendermint/commands/gen_validator.go b/cmd/tendermint/commands/gen_validator.go index a1217e1f0..97c583c22 100644 --- a/cmd/tendermint/commands/gen_validator.go +++ b/cmd/tendermint/commands/gen_validator.go @@ -1,11 +1,11 @@ package commands import ( + "encoding/json" "fmt" "github.com/spf13/cobra" - "github.com/tendermint/go-wire" "github.com/tendermint/tendermint/types" ) @@ -21,7 +21,7 @@ func init() { func genValidator(cmd *cobra.Command, args []string) { privValidator := types.GenPrivValidator() - privValidatorJSONBytes := wire.JSONBytesPretty(privValidator) + privValidatorJSONBytes, _ := json.MarshalIndent(privValidator, "", "\t") fmt.Printf(`%v `, string(privValidatorJSONBytes)) } diff --git a/rpc/core/types/responses.go b/rpc/core/types/responses.go index 23d68587b..7fa9e70b6 100644 --- a/rpc/core/types/responses.go +++ b/rpc/core/types/responses.go @@ -7,7 +7,6 @@ import ( "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire/data" "github.com/tendermint/tendermint/p2p" - // "github.com/tendermint/tendermint/rpc/lib/types" "github.com/tendermint/tendermint/types" ) @@ -121,94 +120,11 @@ type ResultUnsafeSetConfig struct{} type ResultUnsafeProfile struct{} -type ResultSubscribe struct { -} +type ResultSubscribe struct{} -type ResultUnsubscribe struct { -} +type ResultUnsubscribe struct{} type ResultEvent struct { Name string `json:"name"` Data types.TMEventData `json:"data"` } - -//---------------------------------------- -// response & result types - -const ( - // 0x0 bytes are for the blockchain - ResultTypeGenesis = byte(0x01) - ResultTypeBlockchainInfo = byte(0x02) - ResultTypeBlock = byte(0x03) - ResultTypeCommit = byte(0x04) - - // 0x2 bytes are for the network - ResultTypeStatus = byte(0x20) - ResultTypeNetInfo = byte(0x21) - ResultTypeDialSeeds = byte(0x22) - - // 0x4 bytes are for the consensus - ResultTypeValidators = byte(0x40) - ResultTypeDumpConsensusState = byte(0x41) - - // 0x6 bytes are for txs / the application - ResultTypeBroadcastTx = byte(0x60) - ResultTypeUnconfirmedTxs = byte(0x61) - ResultTypeBroadcastTxCommit = byte(0x62) - ResultTypeTx = byte(0x63) - - // 0x7 bytes are for querying the application - ResultTypeABCIQuery = byte(0x70) - ResultTypeABCIInfo = byte(0x71) - - // 0x8 bytes are for events - ResultTypeSubscribe = byte(0x80) - ResultTypeUnsubscribe = byte(0x81) - ResultTypeEvent = byte(0x82) - - // 0xa bytes for testing - ResultTypeUnsafeSetConfig = byte(0xa0) - ResultTypeUnsafeStartCPUProfiler = byte(0xa1) - ResultTypeUnsafeStopCPUProfiler = byte(0xa2) - ResultTypeUnsafeWriteHeapProfile = byte(0xa3) - ResultTypeUnsafeFlushMempool = byte(0xa4) -) - -const ( - // for the blockchain - ResultNameGenesis = "genesis" - ResultNameBlockchainInfo = "info" - ResultNameBlock = "block" - ResultNameCommit = "commit" - - // for the network - ResultNameStatus = "status" - ResultNameNetInfo = "netinfo" - ResultNameDialSeeds = "dialseeds" - - // for the consensus - ResultNameValidators = "validators" - ResultNameDumpConsensusState = "consensus" - - // for txs / the application - ResultNameBroadcastTx = "broadcast_tx" - ResultNameUnconfirmedTxs = "unconfirmed_tx" - ResultNameBroadcastTxCommit = "broadcast_tx_commit" - ResultNameTx = "tx" - - // for querying the application - ResultNameABCIQuery = "abci_query" - ResultNameABCIInfo = "abci_info" - - // for events - ResultNameSubscribe = "subscribe" - ResultNameUnsubscribe = "unsubscribe" - ResultNameEvent = "event" - - // for testing - ResultNameUnsafeSetConfig = "unsafe_set_config" - ResultNameUnsafeStartCPUProfiler = "unsafe_start_profiler" - ResultNameUnsafeStopCPUProfiler = "unsafe_stop_profiler" - ResultNameUnsafeWriteHeapProfile = "unsafe_write_heap" - ResultNameUnsafeFlushMempool = "unsafe_flush_mempool" -) diff --git a/rpc/lib/README.md b/rpc/lib/README.md index 75531963e..6fe44c22f 100644 --- a/rpc/lib/README.md +++ b/rpc/lib/README.md @@ -59,25 +59,17 @@ though this is configurable when starting the server. Define some types and routes: ``` -// Define a type for results and register concrete versions with go-wire -type Result interface{} - type ResultStatus struct { Value string } -var _ = wire.RegisterInterface( - struct{ Result }{}, - wire.ConcreteType{&ResultStatus{}, 0x1}, -) - // Define some routes var Routes = map[string]*rpcserver.RPCFunc{ - "status": rpcserver.NewRPCFunc(StatusResult, "arg"), + "status": rpcserver.NewRPCFunc(Status, "arg"), } // an rpc function -func StatusResult(v string) (Result, error) { +func Status(v string) (*ResultStatus, error) { return &ResultStatus{v}, nil } diff --git a/rpc/lib/client/http_client.go b/rpc/lib/client/http_client.go index 55963f506..5386779d3 100644 --- a/rpc/lib/client/http_client.go +++ b/rpc/lib/client/http_client.go @@ -67,13 +67,6 @@ func NewJSONRPCClient(remote string) *JSONRPCClient { } func (c *JSONRPCClient) Call(method string, params map[string]interface{}, result interface{}) (interface{}, error) { - // we need this step because we attempt to decode values using `go-wire` - // (handlers.go:176) on the server side - // encodedParams := make(map[string]interface{}) - // for k, v := range params { - // bytes := json.RawMessage(wire.JSONBytes(v)) - // encodedParams[k] = &bytes - // } request := types.RPCRequest{ JSONRPC: "2.0", Method: method, diff --git a/rpc/lib/client/ws_client.go b/rpc/lib/client/ws_client.go index 8884068b9..df9cb3812 100644 --- a/rpc/lib/client/ws_client.go +++ b/rpc/lib/client/ws_client.go @@ -154,13 +154,6 @@ func (wsc *WSClient) Unsubscribe(eventid string) error { // Call asynchronously calls a given method by sending an RPCRequest to the // server. Results will be available on ResultsCh, errors, if any, on ErrorsCh. func (wsc *WSClient) Call(method string, params map[string]interface{}) error { - // we need this step because we attempt to decode values using `go-wire` - // (handlers.go:470) on the server side - // encodedParams := make(map[string]interface{}) - // for k, v := range params { - // bytes := json.RawMessage(wire.JSONBytes(v)) - // encodedParams[k] = &bytes - // } err := wsc.WriteJSON(types.RPCRequest{ JSONRPC: "2.0", Method: method, diff --git a/rpc/lib/server/http_server.go b/rpc/lib/server/http_server.go index ae0923c39..b51699c22 100644 --- a/rpc/lib/server/http_server.go +++ b/rpc/lib/server/http_server.go @@ -58,7 +58,6 @@ func WriteRPCResponseHTTPError(w http.ResponseWriter, httpCode int, res types.RP } func WriteRPCResponseHTTP(w http.ResponseWriter, res types.RPCResponse) { - // jsonBytes := wire.JSONBytesPretty(res) jsonBytes, err := json.Marshal(res) if err != nil { panic(err) diff --git a/rpc/lib/types/types.go b/rpc/lib/types/types.go index 28ddfc0ff..a91811e53 100644 --- a/rpc/lib/types/types.go +++ b/rpc/lib/types/types.go @@ -25,22 +25,6 @@ func NewRPCRequest(id string, method string, params map[string]interface{}) RPCR //---------------------------------------- -/* -Result is a generic interface. -Applications should register type-bytes like so: - -var _ = wire.RegisterInterface( - struct{ Result }{}, - wire.ConcreteType{&ResultGenesis{}, ResultTypeGenesis}, - wire.ConcreteType{&ResultBlockchainInfo{}, ResultTypeBlockchainInfo}, - ... -) -*/ -type Result interface { -} - -//---------------------------------------- - type RPCResponse struct { JSONRPC string `json:"jsonrpc"` ID string `json:"id"` diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index a12d82f23..11a228bb1 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -131,11 +131,9 @@ func waitForEvent(t *testing.T, wsc *client.WSClient, eventid string, dieOnTimeo for { select { case r := <-wsc.ResultsCh: - fmt.Println("GOT IT", string(r)) result := new(ctypes.ResultEvent) err = json.Unmarshal(r, result) if err != nil { - fmt.Println("POOP", err) // cant distinguish between error and wrong type ... continue } From 6dbcfb32d29e4924ebb900f49a1450f1aeb4e957 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 28 Apr 2017 23:22:54 -0400 Subject: [PATCH 18/23] comment on copied wire file --- rpc/lib/server/wire.go | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/rpc/lib/server/wire.go b/rpc/lib/server/wire.go index ad3d61f8c..4ed9771a1 100644 --- a/rpc/lib/server/wire.go +++ b/rpc/lib/server/wire.go @@ -13,6 +13,10 @@ import ( cmn "github.com/tendermint/tmlibs/common" ) +// NOTE: This code is copied from go-wire in order to allow byte arrays to be treated +// differently whether they are data.Bytes (hex) or not (base64). We can't build this +// into wire directly without refactoring since we'd need to import data but data imports wire + var ( timeType = wire.GetTypeFromStructDeclaration(struct{ time.Time }{}) ) @@ -128,7 +132,6 @@ func readReflectJSON(rv reflect.Value, rt reflect.Type, opts wire.Options, o int *err = errors.New(cmn.Fmt("Expected bytearray of length %v but got %v", length, len(buf))) return } - //log.Info("Read bytearray", "bytes", buf) reflect.Copy(rv, reflect.ValueOf(buf)) } else { oSlice, ok := o.([]interface{}) @@ -144,7 +147,6 @@ func readReflectJSON(rv reflect.Value, rt reflect.Type, opts wire.Options, o int elemRv := rv.Index(i) readReflectJSON(elemRv, elemRt, opts, oSlice[i], err) } - //log.Info("Read x-array", "x", elemRt, "length", length) } case reflect.Slice: @@ -169,7 +171,6 @@ func readReflectJSON(rv reflect.Value, rt reflect.Type, opts wire.Options, o int *err = err_ return } - //log.Info("Read byteslice", "bytes", byteslice) rv.Set(reflect.ValueOf(buf)) } else { // Read length @@ -179,7 +180,6 @@ func readReflectJSON(rv reflect.Value, rt reflect.Type, opts wire.Options, o int return } length := len(oSlice) - //log.Info("Read slice", "length", length) sliceRv := reflect.MakeSlice(rt, length, length) // Read elems for i := 0; i < length; i++ { @@ -237,7 +237,6 @@ func readReflectJSON(rv reflect.Value, rt reflect.Type, opts wire.Options, o int *err = errors.New(cmn.Fmt("Expected string but got type %v", reflect.TypeOf(o))) return } - //log.Info("Read string", "str", str) rv.SetString(str) case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int: @@ -246,7 +245,6 @@ func readReflectJSON(rv reflect.Value, rt reflect.Type, opts wire.Options, o int *err = errors.New(cmn.Fmt("Expected numeric but got type %v", reflect.TypeOf(o))) return } - //log.Info("Read num", "num", num) rv.SetInt(int64(num)) case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint: @@ -259,7 +257,6 @@ func readReflectJSON(rv reflect.Value, rt reflect.Type, opts wire.Options, o int *err = errors.New(cmn.Fmt("Expected unsigned numeric but got %v", num)) return } - //log.Info("Read num", "num", num) rv.SetUint(uint64(num)) case reflect.Float64, reflect.Float32: @@ -272,7 +269,6 @@ func readReflectJSON(rv reflect.Value, rt reflect.Type, opts wire.Options, o int *err = errors.New(cmn.Fmt("Expected numeric but got type %v", reflect.TypeOf(o))) return } - //log.Info("Read num", "num", num) rv.SetFloat(num) case reflect.Bool: @@ -281,7 +277,6 @@ func readReflectJSON(rv reflect.Value, rt reflect.Type, opts wire.Options, o int *err = errors.New(cmn.Fmt("Expected boolean but got type %v", reflect.TypeOf(o))) return } - //log.Info("Read boolean", "boolean", bl) rv.SetBool(bl) default: From 6ba799132c6423685e1ea113c0b7d0c1fac0d6c3 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 3 May 2017 16:13:58 +0200 Subject: [PATCH 19/23] json.RawMessage in RPCRequest to defer parsing --- rpc/lib/client/http_client.go | 8 +++----- rpc/lib/client/ws_client.go | 32 ++++++++++++++------------------ rpc/lib/rpc_test.go | 9 +++------ rpc/lib/server/handlers.go | 20 ++++++++++++++------ rpc/lib/types/types.go | 30 ++++++++++++++++++++++++------ 5 files changed, 58 insertions(+), 41 deletions(-) diff --git a/rpc/lib/client/http_client.go b/rpc/lib/client/http_client.go index 5386779d3..9ad6386dc 100644 --- a/rpc/lib/client/http_client.go +++ b/rpc/lib/client/http_client.go @@ -67,11 +67,9 @@ func NewJSONRPCClient(remote string) *JSONRPCClient { } func (c *JSONRPCClient) Call(method string, params map[string]interface{}, result interface{}) (interface{}, error) { - request := types.RPCRequest{ - JSONRPC: "2.0", - Method: method, - Params: params, - ID: "", + request, err := types.MapToRequest("", method, params) + if err != nil { + return nil, err } requestBytes, err := json.Marshal(request) if err != nil { diff --git a/rpc/lib/client/ws_client.go b/rpc/lib/client/ws_client.go index df9cb3812..833668d85 100644 --- a/rpc/lib/client/ws_client.go +++ b/rpc/lib/client/ws_client.go @@ -130,35 +130,31 @@ func (wsc *WSClient) receiveEventsRoutine() { // Subscribe to an event. Note the server must have a "subscribe" route // defined. func (wsc *WSClient) Subscribe(eventid string) error { - err := wsc.WriteJSON(types.RPCRequest{ - JSONRPC: "2.0", - ID: "", - Method: "subscribe", - Params: map[string]interface{}{"event": eventid}, - }) + params := map[string]interface{}{"event": eventid} + request, err := types.MapToRequest("", "subscribe", params) + if err == nil { + err = wsc.WriteJSON(request) + } return err } // Unsubscribe from an event. Note the server must have a "unsubscribe" route // defined. func (wsc *WSClient) Unsubscribe(eventid string) error { - err := wsc.WriteJSON(types.RPCRequest{ - JSONRPC: "2.0", - ID: "", - Method: "unsubscribe", - Params: map[string]interface{}{"event": eventid}, - }) + params := map[string]interface{}{"event": eventid} + request, err := types.MapToRequest("", "unsubscribe", params) + if err == nil { + err = wsc.WriteJSON(request) + } return err } // Call asynchronously calls a given method by sending an RPCRequest to the // server. Results will be available on ResultsCh, errors, if any, on ErrorsCh. func (wsc *WSClient) Call(method string, params map[string]interface{}) error { - err := wsc.WriteJSON(types.RPCRequest{ - JSONRPC: "2.0", - Method: method, - Params: params, - ID: "", - }) + request, err := types.MapToRequest("", method, params) + if err == nil { + err = wsc.WriteJSON(request) + } return err } diff --git a/rpc/lib/rpc_test.go b/rpc/lib/rpc_test.go index a14551f4f..3a2b36d51 100644 --- a/rpc/lib/rpc_test.go +++ b/rpc/lib/rpc_test.go @@ -306,12 +306,9 @@ func TestWSHandlesArrayParams(t *testing.T) { val := "acbd" params := []interface{}{val} - err = cl.WriteJSON(types.RPCRequest{ - JSONRPC: "2.0", - ID: "", - Method: "echo_ws", - Params: params, - }) + request, err := types.ArrayToRequest("", "echo_ws", params) + require.Nil(t, err) + err = cl.WriteJSON(request) require.Nil(t, err) select { diff --git a/rpc/lib/server/handlers.go b/rpc/lib/server/handlers.go index 51eaf1401..321b72d6d 100644 --- a/rpc/lib/server/handlers.go +++ b/rpc/lib/server/handlers.go @@ -140,15 +140,23 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc) http.HandlerFunc { } } -// Convert a []interface{} OR a map[string]interface{} to properly typed values +// raw is unparsed json (from json.RawMessage). It either has +// and array or a map behind it, let's parse this all without resorting to wire... // // argsOffset should be 0 for RPC calls, and 1 for WS requests, where len(rpcFunc.args) != len(rpcFunc.argNames). // Example: // rpcFunc.args = [rpctypes.WSRPCContext string] // rpcFunc.argNames = ["arg"] -func jsonParamsToArgs(rpcFunc *RPCFunc, paramsI interface{}, argsOffset int) ([]reflect.Value, error) { +func jsonParamsToArgs(rpcFunc *RPCFunc, raw []byte, argsOffset int) ([]reflect.Value, error) { values := make([]reflect.Value, len(rpcFunc.argNames)) + // right now, this is the same as before, but the whole parsing is in one function... + var paramsI interface{} + err := json.Unmarshal(raw, ¶msI) + if err != nil { + return nil, err + } + switch params := paramsI.(type) { case map[string]interface{}: @@ -188,13 +196,13 @@ func jsonParamsToArgs(rpcFunc *RPCFunc, paramsI interface{}, argsOffset int) ([] } // Convert a []interface{} OR a map[string]interface{} to properly typed values -func jsonParamsToArgsRPC(rpcFunc *RPCFunc, paramsI interface{}) ([]reflect.Value, error) { - return jsonParamsToArgs(rpcFunc, paramsI, 0) +func jsonParamsToArgsRPC(rpcFunc *RPCFunc, params *json.RawMessage) ([]reflect.Value, error) { + return jsonParamsToArgs(rpcFunc, *params, 0) } // Same as above, but with the first param the websocket connection -func jsonParamsToArgsWS(rpcFunc *RPCFunc, paramsI interface{}, wsCtx types.WSRPCContext) ([]reflect.Value, error) { - values, err := jsonParamsToArgs(rpcFunc, paramsI, 1) +func jsonParamsToArgsWS(rpcFunc *RPCFunc, params *json.RawMessage, wsCtx types.WSRPCContext) ([]reflect.Value, error) { + values, err := jsonParamsToArgs(rpcFunc, *params, 1) if err != nil { return nil, err } diff --git a/rpc/lib/types/types.go b/rpc/lib/types/types.go index a91811e53..8076e4b0d 100644 --- a/rpc/lib/types/types.go +++ b/rpc/lib/types/types.go @@ -8,19 +8,37 @@ import ( ) type RPCRequest struct { - JSONRPC string `json:"jsonrpc"` - ID string `json:"id"` - Method string `json:"method"` - Params interface{} `json:"params"` // must be map[string]interface{} or []interface{} + JSONRPC string `json:"jsonrpc"` + ID string `json:"id"` + Method string `json:"method"` + Params *json.RawMessage `json:"params"` // must be map[string]interface{} or []interface{} } -func NewRPCRequest(id string, method string, params map[string]interface{}) RPCRequest { +func NewRPCRequest(id string, method string, params json.RawMessage) RPCRequest { return RPCRequest{ JSONRPC: "2.0", ID: id, Method: method, - Params: params, + Params: ¶ms, + } +} + +func MapToRequest(id string, method string, params map[string]interface{}) (RPCRequest, error) { + payload, err := json.Marshal(params) + if err != nil { + return RPCRequest{}, err + } + request := NewRPCRequest(id, method, payload) + return request, nil +} + +func ArrayToRequest(id string, method string, params []interface{}) (RPCRequest, error) { + payload, err := json.Marshal(params) + if err != nil { + return RPCRequest{}, err } + request := NewRPCRequest(id, method, payload) + return request, nil } //---------------------------------------- From 4c1d41c12ed238d81598cc6c49db43d520c96b9a Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 3 May 2017 16:26:18 +0200 Subject: [PATCH 20/23] Test json rpc parsing --- rpc/lib/server/parse_test.go | 41 ++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/rpc/lib/server/parse_test.go b/rpc/lib/server/parse_test.go index 5b0478b64..3c6d6edde 100644 --- a/rpc/lib/server/parse_test.go +++ b/rpc/lib/server/parse_test.go @@ -2,6 +2,7 @@ package rpcserver import ( "encoding/json" + "strconv" "testing" "github.com/stretchr/testify/assert" @@ -131,3 +132,43 @@ func TestParseJSONArray(t *testing.T) { } } } + +func TestParseRPC(t *testing.T) { + assert := assert.New(t) + + demo := func(height int, name string) {} + call := NewRPCFunc(demo, "height,name") + + cases := []struct { + raw string + height int64 + name string + fail bool + }{ + // should parse + {`[7, "flew"]`, 7, "flew", false}, + {`{"name": "john", "height": 22}`, 22, "john", false}, + // defaults + {`{"name": "solo", "unused": "stuff"}`, 0, "solo", false}, + // should fail - wrong types/lenght + {`["flew", 7]`, 0, "", true}, + {`[7,"flew",100]`, 0, "", true}, + {`{"name": -12, "height": "fred"}`, 0, "", true}, + } + for idx, tc := range cases { + i := strconv.Itoa(idx) + data := []byte(tc.raw) + vals, err := jsonParamsToArgs(call, data, 0) + if tc.fail { + assert.NotNil(err, i) + } else { + assert.Nil(err, "%s: %+v", i, err) + if assert.Equal(2, len(vals), i) { + assert.Equal(tc.height, vals[0].Int(), i) + assert.Equal(tc.name, vals[1].String(), i) + } + } + + } + +} From 4a1b714ca4decaa3fdc3b08c855220ad09aa8121 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 3 May 2017 16:42:30 +0200 Subject: [PATCH 21/23] All tests pass without go-wire json ptr madness --- rpc/lib/server/handlers.go | 106 +++++++------- rpc/lib/server/wire.go | 285 ------------------------------------- 2 files changed, 53 insertions(+), 338 deletions(-) delete mode 100644 rpc/lib/server/wire.go diff --git a/rpc/lib/server/handlers.go b/rpc/lib/server/handlers.go index 321b72d6d..588af2765 100644 --- a/rpc/lib/server/handlers.go +++ b/rpc/lib/server/handlers.go @@ -15,6 +15,7 @@ import ( "github.com/gorilla/websocket" "github.com/pkg/errors" //wire "github.com/tendermint/go-wire" + types "github.com/tendermint/tendermint/rpc/lib/types" cmn "github.com/tendermint/tmlibs/common" events "github.com/tendermint/tmlibs/events" @@ -140,6 +141,45 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc) http.HandlerFunc { } } +func mapParamsToArgs(rpcFunc *RPCFunc, params map[string]*json.RawMessage, argsOffset int) ([]reflect.Value, error) { + values := make([]reflect.Value, len(rpcFunc.argNames)) + for i, argName := range rpcFunc.argNames { + argType := rpcFunc.args[i+argsOffset] + + if p, ok := params[argName]; ok && len(*p) > 0 { + val := reflect.New(argType) + err := json.Unmarshal(*p, val.Interface()) + if err != nil { + return nil, err + } + values[i] = val.Elem() + } else { // use default for that type + values[i] = reflect.Zero(argType) + } + } + + return values, nil +} + +func arrayParamsToArgs(rpcFunc *RPCFunc, params []*json.RawMessage, argsOffset int) ([]reflect.Value, error) { + if len(rpcFunc.argNames) != len(params) { + return nil, errors.Errorf("Expected %v parameters (%v), got %v (%v)", + len(rpcFunc.argNames), rpcFunc.argNames, len(params), params) + } + + values := make([]reflect.Value, len(params)) + for i, p := range params { + argType := rpcFunc.args[i+argsOffset] + val := reflect.New(argType) + err := json.Unmarshal(*p, val.Interface()) + if err != nil { + return nil, err + } + values[i] = val.Elem() + } + return values, nil +} + // raw is unparsed json (from json.RawMessage). It either has // and array or a map behind it, let's parse this all without resorting to wire... // @@ -148,51 +188,22 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc) http.HandlerFunc { // rpcFunc.args = [rpctypes.WSRPCContext string] // rpcFunc.argNames = ["arg"] func jsonParamsToArgs(rpcFunc *RPCFunc, raw []byte, argsOffset int) ([]reflect.Value, error) { - values := make([]reflect.Value, len(rpcFunc.argNames)) - - // right now, this is the same as before, but the whole parsing is in one function... - var paramsI interface{} - err := json.Unmarshal(raw, ¶msI) - if err != nil { - return nil, err + // first, try to get the map.. + var m map[string]*json.RawMessage + err := json.Unmarshal(raw, &m) + if err == nil { + return mapParamsToArgs(rpcFunc, m, argsOffset) } - switch params := paramsI.(type) { - - case map[string]interface{}: - for i, argName := range rpcFunc.argNames { - argType := rpcFunc.args[i+argsOffset] - - // decode param if provided - if param, ok := params[argName]; ok && "" != param { - v, err := _jsonObjectToArg(argType, param) - if err != nil { - return nil, err - } - values[i] = v - } else { // use default for that type - values[i] = reflect.Zero(argType) - } - } - case []interface{}: - if len(rpcFunc.argNames) != len(params) { - return nil, errors.New(fmt.Sprintf("Expected %v parameters (%v), got %v (%v)", - len(rpcFunc.argNames), rpcFunc.argNames, len(params), params)) - } - values := make([]reflect.Value, len(params)) - for i, p := range params { - ty := rpcFunc.args[i+argsOffset] - v, err := _jsonObjectToArg(ty, p) - if err != nil { - return nil, err - } - values[i] = v - } - return values, nil - default: - return nil, fmt.Errorf("Unknown type for JSON params %v. Expected map[string]interface{} or []interface{}", reflect.TypeOf(paramsI)) + // otherwise, try an array + var a []*json.RawMessage + err = json.Unmarshal(raw, &a) + if err == nil { + return arrayParamsToArgs(rpcFunc, a, argsOffset) } - return values, nil + + // otherwise, bad format, we cannot parse + return nil, errors.Errorf("Unknown type for JSON params: %v. Expected map or array", err) } // Convert a []interface{} OR a map[string]interface{} to properly typed values @@ -209,17 +220,6 @@ func jsonParamsToArgsWS(rpcFunc *RPCFunc, params *json.RawMessage, wsCtx types.W return append([]reflect.Value{reflect.ValueOf(wsCtx)}, values...), nil } -func _jsonObjectToArg(ty reflect.Type, object interface{}) (reflect.Value, error) { - var err error - v := reflect.New(ty) - readJSONObjectPtr(v.Interface(), object, &err) - if err != nil { - return v, err - } - v = v.Elem() - return v, nil -} - // rpc.json //----------------------------------------------------------------------------- // rpc.http diff --git a/rpc/lib/server/wire.go b/rpc/lib/server/wire.go deleted file mode 100644 index 4ed9771a1..000000000 --- a/rpc/lib/server/wire.go +++ /dev/null @@ -1,285 +0,0 @@ -package rpcserver - -import ( - "encoding/base64" - "encoding/hex" - "reflect" - "time" - - "github.com/pkg/errors" - - "github.com/tendermint/go-wire" - "github.com/tendermint/go-wire/data" - cmn "github.com/tendermint/tmlibs/common" -) - -// NOTE: This code is copied from go-wire in order to allow byte arrays to be treated -// differently whether they are data.Bytes (hex) or not (base64). We can't build this -// into wire directly without refactoring since we'd need to import data but data imports wire - -var ( - timeType = wire.GetTypeFromStructDeclaration(struct{ time.Time }{}) -) - -func readJSONObjectPtr(o interface{}, object interface{}, err *error) interface{} { - rv, rt := reflect.ValueOf(o), reflect.TypeOf(o) - if rv.Kind() == reflect.Ptr { - readReflectJSON(rv.Elem(), rt.Elem(), wire.Options{}, object, err) - } else { - cmn.PanicSanity("ReadJSON(Object)Ptr expects o to be a pointer") - } - return o -} - -func readByteJSON(o interface{}) (typeByte byte, rest interface{}, err error) { - oSlice, ok := o.([]interface{}) - if !ok { - err = errors.New(cmn.Fmt("Expected type [Byte,?] but got type %v", reflect.TypeOf(o))) - return - } - if len(oSlice) != 2 { - err = errors.New(cmn.Fmt("Expected [Byte,?] len 2 but got len %v", len(oSlice))) - return - } - typeByte_, ok := oSlice[0].(float64) - typeByte = byte(typeByte_) - rest = oSlice[1] - return -} - -// Contract: Caller must ensure that rt is supported -// (e.g. is recursively composed of supported native types, and structs and slices.) -// rv and rt refer to the object we're unmarhsaling into, whereas o is the result of naiive json unmarshal (map[string]interface{}) -func readReflectJSON(rv reflect.Value, rt reflect.Type, opts wire.Options, o interface{}, err *error) { - - // Get typeInfo - typeInfo := wire.GetTypeInfo(rt) - - if rt.Kind() == reflect.Interface { - if !typeInfo.IsRegisteredInterface { - // There's no way we can read such a thing. - *err = errors.New(cmn.Fmt("Cannot read unregistered interface type %v", rt)) - return - } - if o == nil { - return // nil - } - typeByte, rest, err_ := readByteJSON(o) - if err_ != nil { - *err = err_ - return - } - crt, ok := typeInfo.ByteToType[typeByte] - if !ok { - *err = errors.New(cmn.Fmt("Byte %X not registered for interface %v", typeByte, rt)) - return - } - if crt.Kind() == reflect.Ptr { - crt = crt.Elem() - crv := reflect.New(crt) - readReflectJSON(crv.Elem(), crt, opts, rest, err) - rv.Set(crv) // NOTE: orig rv is ignored. - } else { - crv := reflect.New(crt).Elem() - readReflectJSON(crv, crt, opts, rest, err) - rv.Set(crv) // NOTE: orig rv is ignored. - } - return - } - - if rt.Kind() == reflect.Ptr { - if o == nil { - return // nil - } - // Create new struct if rv is nil. - if rv.IsNil() { - newRv := reflect.New(rt.Elem()) - rv.Set(newRv) - rv = newRv - } - // Dereference pointer - rv, rt = rv.Elem(), rt.Elem() - typeInfo = wire.GetTypeInfo(rt) - // continue... - } - - switch rt.Kind() { - case reflect.Array: - elemRt := rt.Elem() - length := rt.Len() - if elemRt.Kind() == reflect.Uint8 { - // Special case: Bytearrays - oString, ok := o.(string) - if !ok { - *err = errors.New(cmn.Fmt("Expected string but got type %v", reflect.TypeOf(o))) - return - } - - // if its data.Bytes, use hex; else use base64 - dbty := reflect.TypeOf(data.Bytes{}) - var buf []byte - var err_ error - if rt == dbty { - buf, err_ = hex.DecodeString(oString) - } else { - buf, err_ = base64.StdEncoding.DecodeString(oString) - } - if err_ != nil { - *err = err_ - return - } - if len(buf) != length { - *err = errors.New(cmn.Fmt("Expected bytearray of length %v but got %v", length, len(buf))) - return - } - reflect.Copy(rv, reflect.ValueOf(buf)) - } else { - oSlice, ok := o.([]interface{}) - if !ok { - *err = errors.New(cmn.Fmt("Expected array of %v but got type %v", rt, reflect.TypeOf(o))) - return - } - if len(oSlice) != length { - *err = errors.New(cmn.Fmt("Expected array of length %v but got %v", length, len(oSlice))) - return - } - for i := 0; i < length; i++ { - elemRv := rv.Index(i) - readReflectJSON(elemRv, elemRt, opts, oSlice[i], err) - } - } - - case reflect.Slice: - elemRt := rt.Elem() - if elemRt.Kind() == reflect.Uint8 { - // Special case: Byteslices - oString, ok := o.(string) - if !ok { - *err = errors.New(cmn.Fmt("Expected string but got type %v", reflect.TypeOf(o))) - return - } - // if its data.Bytes, use hex; else use base64 - dbty := reflect.TypeOf(data.Bytes{}) - var buf []byte - var err_ error - if rt == dbty { - buf, err_ = hex.DecodeString(oString) - } else { - buf, err_ = base64.StdEncoding.DecodeString(oString) - } - if err_ != nil { - *err = err_ - return - } - rv.Set(reflect.ValueOf(buf)) - } else { - // Read length - oSlice, ok := o.([]interface{}) - if !ok { - *err = errors.New(cmn.Fmt("Expected array of %v but got type %v", rt, reflect.TypeOf(o))) - return - } - length := len(oSlice) - sliceRv := reflect.MakeSlice(rt, length, length) - // Read elems - for i := 0; i < length; i++ { - elemRv := sliceRv.Index(i) - readReflectJSON(elemRv, elemRt, opts, oSlice[i], err) - } - rv.Set(sliceRv) - } - - case reflect.Struct: - if rt == timeType { - // Special case: time.Time - str, ok := o.(string) - if !ok { - *err = errors.New(cmn.Fmt("Expected string but got type %v", reflect.TypeOf(o))) - return - } - // try three ways, seconds, milliseconds, or microseconds... - t, err_ := time.Parse(time.RFC3339Nano, str) - if err_ != nil { - *err = err_ - return - } - rv.Set(reflect.ValueOf(t)) - } else { - if typeInfo.Unwrap { - f := typeInfo.Fields[0] - fieldIdx, fieldType, opts := f.Index, f.Type, f.Options - fieldRv := rv.Field(fieldIdx) - readReflectJSON(fieldRv, fieldType, opts, o, err) - } else { - oMap, ok := o.(map[string]interface{}) - if !ok { - *err = errors.New(cmn.Fmt("Expected map but got type %v", reflect.TypeOf(o))) - return - } - // TODO: ensure that all fields are set? - // TODO: disallow unknown oMap fields? - for _, fieldInfo := range typeInfo.Fields { - f := fieldInfo - fieldIdx, fieldType, opts := f.Index, f.Type, f.Options - value, ok := oMap[opts.JSONName] - if !ok { - continue // Skip missing fields. - } - fieldRv := rv.Field(fieldIdx) - readReflectJSON(fieldRv, fieldType, opts, value, err) - } - } - } - - case reflect.String: - str, ok := o.(string) - if !ok { - *err = errors.New(cmn.Fmt("Expected string but got type %v", reflect.TypeOf(o))) - return - } - rv.SetString(str) - - case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int: - num, ok := o.(float64) - if !ok { - *err = errors.New(cmn.Fmt("Expected numeric but got type %v", reflect.TypeOf(o))) - return - } - rv.SetInt(int64(num)) - - case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint: - num, ok := o.(float64) - if !ok { - *err = errors.New(cmn.Fmt("Expected numeric but got type %v", reflect.TypeOf(o))) - return - } - if num < 0 { - *err = errors.New(cmn.Fmt("Expected unsigned numeric but got %v", num)) - return - } - rv.SetUint(uint64(num)) - - case reflect.Float64, reflect.Float32: - if !opts.Unsafe { - *err = errors.New("Wire float* support requires `wire:\"unsafe\"`") - return - } - num, ok := o.(float64) - if !ok { - *err = errors.New(cmn.Fmt("Expected numeric but got type %v", reflect.TypeOf(o))) - return - } - rv.SetFloat(num) - - case reflect.Bool: - bl, ok := o.(bool) - if !ok { - *err = errors.New(cmn.Fmt("Expected boolean but got type %v", reflect.TypeOf(o))) - return - } - rv.SetBool(bl) - - default: - cmn.PanicSanity(cmn.Fmt("Unknown field type %v", rt.Kind())) - } -} From 7ebf011fcd200ee51bfbb5315ed9ebb9f8c02989 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 3 May 2017 16:58:21 +0200 Subject: [PATCH 22/23] Fixed rpctypes.Request creation to new format --- benchmarks/simu/counter.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/benchmarks/simu/counter.go b/benchmarks/simu/counter.go index a22162b97..e9502f956 100644 --- a/benchmarks/simu/counter.go +++ b/benchmarks/simu/counter.go @@ -7,11 +7,11 @@ import ( "fmt" "github.com/gorilla/websocket" - . "github.com/tendermint/tmlibs/common" - "github.com/tendermint/tendermint/rpc/lib/client" - "github.com/tendermint/tendermint/rpc/lib/types" "github.com/tendermint/go-wire" _ "github.com/tendermint/tendermint/rpc/core/types" // Register RPCResponse > Result types + "github.com/tendermint/tendermint/rpc/lib/client" + "github.com/tendermint/tendermint/rpc/lib/types" + . "github.com/tendermint/tmlibs/common" ) func main() { @@ -37,13 +37,16 @@ func main() { for i := 0; ; i++ { binary.BigEndian.PutUint64(buf, uint64(i)) //txBytes := hex.EncodeToString(buf[:n]) - request := rpctypes.NewRPCRequest("fakeid", + request, err := rpctypes.MapToRequest("fakeid", "broadcast_tx", map[string]interface{}{"tx": buf[:8]}) + if err != nil { + Exit(err.Error()) + } reqBytes := wire.JSONBytes(request) //fmt.Println("!!", string(reqBytes)) fmt.Print(".") - err := ws.WriteMessage(websocket.TextMessage, reqBytes) + err = ws.WriteMessage(websocket.TextMessage, reqBytes) if err != nil { Exit(err.Error()) } From 9860c8fee14bfd4f6f14b62ee6d8e938a485387a Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 3 May 2017 14:33:00 -0400 Subject: [PATCH 23/23] rpc: cleanup some comments [ci skip] --- rpc/lib/server/handlers.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/rpc/lib/server/handlers.go b/rpc/lib/server/handlers.go index 588af2765..f780fba44 100644 --- a/rpc/lib/server/handlers.go +++ b/rpc/lib/server/handlers.go @@ -14,7 +14,6 @@ import ( "github.com/gorilla/websocket" "github.com/pkg/errors" - //wire "github.com/tendermint/go-wire" types "github.com/tendermint/tendermint/rpc/lib/types" cmn "github.com/tendermint/tmlibs/common" @@ -180,8 +179,7 @@ func arrayParamsToArgs(rpcFunc *RPCFunc, params []*json.RawMessage, argsOffset i return values, nil } -// raw is unparsed json (from json.RawMessage). It either has -// and array or a map behind it, let's parse this all without resorting to wire... +// raw is unparsed json (from json.RawMessage) encoding either a map or an array. // // argsOffset should be 0 for RPC calls, and 1 for WS requests, where len(rpcFunc.args) != len(rpcFunc.argNames). // Example: @@ -277,7 +275,6 @@ func httpParamsToArgs(rpcFunc *RPCFunc, r *http.Request) ([]reflect.Value, error continue } - // Pass values to go-wire values[i], err = _jsonStringToArg(argType, arg) if err != nil { return nil, err