diff --git a/cmd/tendermint/commands/gen_node_key.go b/cmd/tendermint/commands/gen_node_key.go index d8b493e3c..1207c704b 100644 --- a/cmd/tendermint/commands/gen_node_key.go +++ b/cmd/tendermint/commands/gen_node_key.go @@ -1,11 +1,11 @@ package commands import ( + "encoding/json" "fmt" "github.com/spf13/cobra" - tmjson "github.com/tendermint/tendermint/libs/json" "github.com/tendermint/tendermint/types" ) @@ -20,7 +20,7 @@ var GenNodeKeyCmd = &cobra.Command{ func genNodeKey(cmd *cobra.Command, args []string) error { nodeKey := types.GenNodeKey() - bz, err := tmjson.Marshal(nodeKey) + bz, err := json.Marshal(nodeKey) if err != nil { return fmt.Errorf("nodeKey -> json: %w", err) } diff --git a/cmd/tendermint/commands/gen_validator.go b/cmd/tendermint/commands/gen_validator.go index 830518ce9..450efad85 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" - tmjson "github.com/tendermint/tendermint/libs/json" "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/types" ) @@ -29,7 +29,7 @@ func genValidator(cmd *cobra.Command, args []string) error { return err } - jsbz, err := tmjson.Marshal(pv) + jsbz, err := json.Marshal(pv) if err != nil { return fmt.Errorf("validator -> json: %w", err) } diff --git a/cmd/tendermint/commands/show_validator.go b/cmd/tendermint/commands/show_validator.go index b6b9c67ec..d09fce155 100644 --- a/cmd/tendermint/commands/show_validator.go +++ b/cmd/tendermint/commands/show_validator.go @@ -7,7 +7,7 @@ import ( "github.com/spf13/cobra" "github.com/tendermint/tendermint/crypto" - tmjson "github.com/tendermint/tendermint/libs/json" + "github.com/tendermint/tendermint/internal/jsontypes" tmnet "github.com/tendermint/tendermint/libs/net" tmos "github.com/tendermint/tendermint/libs/os" "github.com/tendermint/tendermint/privval" @@ -70,7 +70,7 @@ func showValidator(cmd *cobra.Command, args []string) error { } } - bz, err := tmjson.Marshal(pubKey) + bz, err := jsontypes.Marshal(pubKey) if err != nil { return fmt.Errorf("failed to marshal private validator pubkey: %w", err) } diff --git a/config/config.go b/config/config.go index a559e4149..3758c9241 100644 --- a/config/config.go +++ b/config/config.go @@ -2,6 +2,7 @@ package config import ( "encoding/hex" + "encoding/json" "errors" "fmt" "net/http" @@ -9,7 +10,6 @@ import ( "path/filepath" "time" - tmjson "github.com/tendermint/tendermint/libs/json" "github.com/tendermint/tendermint/libs/log" tmos "github.com/tendermint/tendermint/libs/os" "github.com/tendermint/tendermint/types" @@ -270,7 +270,7 @@ func (cfg BaseConfig) LoadNodeKeyID() (types.NodeID, error) { return "", err } nodeKey := types.NodeKey{} - err = tmjson.Unmarshal(jsonBytes, &nodeKey) + err = json.Unmarshal(jsonBytes, &nodeKey) if err != nil { return "", err } diff --git a/crypto/ed25519/ed25519.go b/crypto/ed25519/ed25519.go index 8673ff4d5..ffd4a3ed1 100644 --- a/crypto/ed25519/ed25519.go +++ b/crypto/ed25519/ed25519.go @@ -13,7 +13,6 @@ import ( "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/tmhash" "github.com/tendermint/tendermint/internal/jsontypes" - tmjson "github.com/tendermint/tendermint/libs/json" ) //------------------------------------- @@ -57,9 +56,6 @@ const ( ) func init() { - tmjson.RegisterType(PubKey{}, PubKeyName) - tmjson.RegisterType(PrivKey{}, PrivKeyName) - jsontypes.MustRegister(PubKey{}) jsontypes.MustRegister(PrivKey{}) } diff --git a/crypto/secp256k1/secp256k1.go b/crypto/secp256k1/secp256k1.go index f92b29c1f..f212874c7 100644 --- a/crypto/secp256k1/secp256k1.go +++ b/crypto/secp256k1/secp256k1.go @@ -11,7 +11,6 @@ import ( secp256k1 "github.com/btcsuite/btcd/btcec" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/internal/jsontypes" - tmjson "github.com/tendermint/tendermint/libs/json" // necessary for Bitcoin address format "golang.org/x/crypto/ripemd160" // nolint @@ -27,9 +26,6 @@ const ( ) func init() { - tmjson.RegisterType(PubKey{}, PubKeyName) - tmjson.RegisterType(PrivKey{}, PrivKeyName) - jsontypes.MustRegister(PubKey{}) jsontypes.MustRegister(PrivKey{}) } diff --git a/crypto/sr25519/encoding.go b/crypto/sr25519/encoding.go index 8827ee0b1..7ff110821 100644 --- a/crypto/sr25519/encoding.go +++ b/crypto/sr25519/encoding.go @@ -2,7 +2,6 @@ package sr25519 import ( "github.com/tendermint/tendermint/internal/jsontypes" - tmjson "github.com/tendermint/tendermint/libs/json" ) const ( @@ -11,9 +10,6 @@ const ( ) func init() { - tmjson.RegisterType(PubKey{}, PubKeyName) - tmjson.RegisterType(PrivKey{}, PrivKeyName) - jsontypes.MustRegister(PubKey{}) jsontypes.MustRegister(PrivKey{}) } diff --git a/internal/consensus/peer_state.go b/internal/consensus/peer_state.go index 79f4f4430..7f3788cb4 100644 --- a/internal/consensus/peer_state.go +++ b/internal/consensus/peer_state.go @@ -1,6 +1,7 @@ package consensus import ( + "encoding/json" "errors" "fmt" "sync" @@ -9,7 +10,6 @@ import ( cstypes "github.com/tendermint/tendermint/internal/consensus/types" tmsync "github.com/tendermint/tendermint/internal/libs/sync" "github.com/tendermint/tendermint/libs/bits" - tmjson "github.com/tendermint/tendermint/libs/json" "github.com/tendermint/tendermint/libs/log" tmtime "github.com/tendermint/tendermint/libs/time" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" @@ -96,8 +96,7 @@ func (ps *PeerState) GetRoundState() *cstypes.PeerRoundState { func (ps *PeerState) ToJSON() ([]byte, error) { ps.mtx.Lock() defer ps.mtx.Unlock() - - return tmjson.Marshal(ps) + return json.Marshal(ps) } // GetHeight returns an atomic snapshot of the PeerRoundState's height used by diff --git a/internal/jsontypes/jsontypes.go b/internal/jsontypes/jsontypes.go index 16095ee2f..69405da1b 100644 --- a/internal/jsontypes/jsontypes.go +++ b/internal/jsontypes/jsontypes.go @@ -84,6 +84,10 @@ func Unmarshal(data []byte, v interface{}) error { return fmt.Errorf("target is a nil %T", v) } baseType := target.Type().Elem() + if isNull(data) { + target.Elem().Set(reflect.Zero(baseType)) + return nil + } var w wrapper dec := json.NewDecoder(bytes.NewReader(data)) @@ -93,7 +97,7 @@ func Unmarshal(data []byte, v interface{}) error { } typ, ok := registry.types[w.Type] if !ok { - return fmt.Errorf("unknown type tag: %q", w.Type) + return fmt.Errorf("unknown type tag for %T: %q", v, w.Type) } if typ.AssignableTo(baseType) { // ok: registered type is directly assignable to the target @@ -110,3 +114,8 @@ func Unmarshal(data []byte, v interface{}) error { target.Elem().Set(obj.Elem()) return nil } + +// isNull reports true if data is empty or is the JSON "null" value. +func isNull(data []byte) bool { + return len(data) == 0 || bytes.Equal(data, []byte("null")) +} diff --git a/internal/rpc/core/env.go b/internal/rpc/core/env.go index b91d73ebc..448cd85a0 100644 --- a/internal/rpc/core/env.go +++ b/internal/rpc/core/env.go @@ -3,6 +3,7 @@ package core import ( "context" "encoding/base64" + "encoding/json" "fmt" "net" "net/http" @@ -21,7 +22,6 @@ import ( sm "github.com/tendermint/tendermint/internal/state" "github.com/tendermint/tendermint/internal/state/indexer" "github.com/tendermint/tendermint/internal/statesync" - tmjson "github.com/tendermint/tendermint/libs/json" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/libs/strings" "github.com/tendermint/tendermint/rpc/coretypes" @@ -154,7 +154,7 @@ func (env *Environment) InitGenesisChunks() error { return nil } - data, err := tmjson.Marshal(env.GenDoc) + data, err := json.Marshal(env.GenDoc) if err != nil { return err } diff --git a/internal/rpc/core/evidence.go b/internal/rpc/core/evidence.go index f85892d99..e97024b4c 100644 --- a/internal/rpc/core/evidence.go +++ b/internal/rpc/core/evidence.go @@ -5,25 +5,22 @@ import ( "fmt" "github.com/tendermint/tendermint/rpc/coretypes" - "github.com/tendermint/tendermint/types" ) // BroadcastEvidence broadcasts evidence of the misbehavior. // More: https://docs.tendermint.com/master/rpc/#/Evidence/broadcast_evidence func (env *Environment) BroadcastEvidence( ctx context.Context, - ev types.Evidence) (*coretypes.ResultBroadcastEvidence, error) { - - if ev == nil { + ev coretypes.Evidence, +) (*coretypes.ResultBroadcastEvidence, error) { + if ev.Value == nil { return nil, fmt.Errorf("%w: no evidence was provided", coretypes.ErrInvalidRequest) } - - if err := ev.ValidateBasic(); err != nil { + if err := ev.Value.ValidateBasic(); err != nil { return nil, fmt.Errorf("evidence.ValidateBasic failed: %w", err) } - - if err := env.EvidencePool.AddEvidence(ev); err != nil { + if err := env.EvidencePool.AddEvidence(ev.Value); err != nil { return nil, fmt.Errorf("failed to add evidence: %w", err) } - return &coretypes.ResultBroadcastEvidence{Hash: ev.Hash()}, nil + return &coretypes.ResultBroadcastEvidence{Hash: ev.Value.Hash()}, nil } diff --git a/internal/rpc/core/routes.go b/internal/rpc/core/routes.go index 09be47c5c..84e8e199a 100644 --- a/internal/rpc/core/routes.go +++ b/internal/rpc/core/routes.go @@ -86,7 +86,7 @@ type RPCService interface { BlockResults(ctx context.Context, heightPtr *int64) (*coretypes.ResultBlockResults, error) BlockSearch(ctx context.Context, query string, pagePtr, perPagePtr *int, orderBy string) (*coretypes.ResultBlockSearch, error) BlockchainInfo(ctx context.Context, minHeight, maxHeight int64) (*coretypes.ResultBlockchainInfo, error) - BroadcastEvidence(ctx context.Context, ev types.Evidence) (*coretypes.ResultBroadcastEvidence, error) + BroadcastEvidence(ctx context.Context, ev coretypes.Evidence) (*coretypes.ResultBroadcastEvidence, error) BroadcastTxAsync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTxCommit, error) BroadcastTxSync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error) diff --git a/light/proxy/routes.go b/light/proxy/routes.go index 76cc52f73..24e7e18c2 100644 --- a/light/proxy/routes.go +++ b/light/proxy/routes.go @@ -38,3 +38,7 @@ func (p proxyService) Unsubscribe(ctx context.Context, query string) (*coretypes func (p proxyService) UnsubscribeAll(ctx context.Context) (*coretypes.ResultUnsubscribe, error) { return p.UnsubscribeAllWS(ctx) } + +func (p proxyService) BroadcastEvidence(ctx context.Context, ev coretypes.Evidence) (*coretypes.ResultBroadcastEvidence, error) { + return p.Client.BroadcastEvidence(ctx, ev.Value) +} diff --git a/privval/file.go b/privval/file.go index d5c959b9b..728e0dc67 100644 --- a/privval/file.go +++ b/privval/file.go @@ -18,7 +18,6 @@ import ( "github.com/tendermint/tendermint/internal/libs/protoio" "github.com/tendermint/tendermint/internal/libs/tempfile" tmbytes "github.com/tendermint/tendermint/libs/bytes" - tmjson "github.com/tendermint/tendermint/libs/json" tmos "github.com/tendermint/tendermint/libs/os" tmtime "github.com/tendermint/tendermint/libs/time" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" @@ -49,13 +48,19 @@ func voteToStep(vote *tmproto.Vote) (int8, error) { // FilePVKey stores the immutable part of PrivValidator. type FilePVKey struct { - Address types.Address `json:"address"` - PubKey crypto.PubKey `json:"pub_key"` - PrivKey crypto.PrivKey `json:"priv_key"` + Address types.Address + PubKey crypto.PubKey + PrivKey crypto.PrivKey filePath string } +type filePVKeyJSON struct { + Address types.Address `json:"address"` + PubKey json.RawMessage `json:"pub_key"` + PrivKey json.RawMessage `json:"priv_key"` +} + func (pvKey FilePVKey) MarshalJSON() ([]byte, error) { pubk, err := jsontypes.Marshal(pvKey.PubKey) if err != nil { @@ -65,11 +70,24 @@ func (pvKey FilePVKey) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - return json.Marshal(struct { - Address types.Address `json:"address"` - PubKey json.RawMessage `json:"pub_key"` - PrivKey json.RawMessage `json:"priv_key"` - }{Address: pvKey.Address, PubKey: pubk, PrivKey: privk}) + return json.Marshal(filePVKeyJSON{ + Address: pvKey.Address, PubKey: pubk, PrivKey: privk, + }) +} + +func (pvKey *FilePVKey) UnmarshalJSON(data []byte) error { + var key filePVKeyJSON + if err := json.Unmarshal(data, &key); err != nil { + return err + } + if err := jsontypes.Unmarshal(key.PubKey, &pvKey.PubKey); err != nil { + return fmt.Errorf("decoding PubKey: %w", err) + } + if err := jsontypes.Unmarshal(key.PrivKey, &pvKey.PrivKey); err != nil { + return fmt.Errorf("decoding PrivKey: %w", err) + } + pvKey.Address = key.Address + return nil } // Save persists the FilePVKey to its filePath. @@ -79,11 +97,11 @@ func (pvKey FilePVKey) Save() error { return errors.New("cannot save PrivValidator key: filePath not set") } - jsonBytes, err := tmjson.MarshalIndent(pvKey, "", " ") + data, err := json.MarshalIndent(pvKey, "", " ") if err != nil { return err } - return tempfile.WriteFileAtomic(outFile, jsonBytes, 0600) + return tempfile.WriteFileAtomic(outFile, data, 0600) } //------------------------------------------------------------------------------- @@ -216,7 +234,7 @@ func loadFilePV(keyFilePath, stateFilePath string, loadState bool) (*FilePV, err return nil, err } pvKey := FilePVKey{} - err = tmjson.Unmarshal(keyJSONBytes, &pvKey) + err = json.Unmarshal(keyJSONBytes, &pvKey) if err != nil { return nil, fmt.Errorf("error reading PrivValidator key from %v: %w", keyFilePath, err) } diff --git a/privval/file_test.go b/privval/file_test.go index e51b488cb..9e0c3d691 100644 --- a/privval/file_test.go +++ b/privval/file_test.go @@ -14,7 +14,6 @@ import ( "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/tmhash" - tmjson "github.com/tendermint/tendermint/libs/json" tmrand "github.com/tendermint/tendermint/libs/rand" tmtime "github.com/tendermint/tendermint/libs/time" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" @@ -143,7 +142,7 @@ func TestUnmarshalValidatorKey(t *testing.T) { }`, addr, pubB64, privB64) val := FilePVKey{} - err := tmjson.Unmarshal([]byte(serialized), &val) + err := json.Unmarshal([]byte(serialized), &val) require.NoError(t, err) // make sure the values match @@ -152,7 +151,7 @@ func TestUnmarshalValidatorKey(t *testing.T) { assert.EqualValues(t, privKey, val.PrivKey) // export it and make sure it is the same - out, err := tmjson.Marshal(val) + out, err := json.Marshal(val) require.NoError(t, err) assert.JSONEq(t, serialized, string(out)) } diff --git a/rpc/client/http/http.go b/rpc/client/http/http.go index f3ae8fe1c..523253523 100644 --- a/rpc/client/http/http.go +++ b/rpc/client/http/http.go @@ -512,7 +512,7 @@ func (c *baseRPCClient) BroadcastEvidence( ) (*coretypes.ResultBroadcastEvidence, error) { result := new(coretypes.ResultBroadcastEvidence) if err := c.caller.Call(ctx, "broadcast_evidence", evidenceArgs{ - Evidence: ev, + Evidence: coretypes.Evidence{Value: ev}, }, result); err != nil { return nil, err } diff --git a/rpc/client/http/request.go b/rpc/client/http/request.go index 88d6b1d1b..746cb776d 100644 --- a/rpc/client/http/request.go +++ b/rpc/client/http/request.go @@ -4,11 +4,8 @@ package http // from the client to the server. import ( - "encoding/json" - - "github.com/tendermint/tendermint/internal/jsontypes" "github.com/tendermint/tendermint/libs/bytes" - "github.com/tendermint/tendermint/types" + "github.com/tendermint/tendermint/rpc/coretypes" ) type abciQueryArgs struct { @@ -64,17 +61,5 @@ type validatorArgs struct { } type evidenceArgs struct { - Evidence types.Evidence -} - -// MarshalJSON implements json.Marshaler to encode the evidence using the -// wrapped concrete type of the implementation. -func (e evidenceArgs) MarshalJSON() ([]byte, error) { - ev, err := jsontypes.Marshal(e.Evidence) - if err != nil { - return nil, err - } - return json.Marshal(struct { - Evidence json.RawMessage `json:"evidence"` - }{Evidence: ev}) + Evidence coretypes.Evidence `json:"evidence"` } diff --git a/rpc/client/http/ws.go b/rpc/client/http/ws.go index ceda36c3c..166a6e990 100644 --- a/rpc/client/http/ws.go +++ b/rpc/client/http/ws.go @@ -2,6 +2,7 @@ package http import ( "context" + "encoding/json" "errors" "fmt" "strings" @@ -9,7 +10,6 @@ import ( "time" "github.com/tendermint/tendermint/internal/pubsub" - tmjson "github.com/tendermint/tendermint/libs/json" rpcclient "github.com/tendermint/tendermint/rpc/client" "github.com/tendermint/tendermint/rpc/coretypes" jsonrpcclient "github.com/tendermint/tendermint/rpc/jsonrpc/client" @@ -239,7 +239,7 @@ func (w *wsEvents) eventListener(ctx context.Context) { } result := new(coretypes.ResultEvent) - err := tmjson.Unmarshal(resp.Result, result) + err := json.Unmarshal(resp.Result, result) if err != nil { w.Logger.Error("failed to unmarshal response", "err", err) continue diff --git a/rpc/client/local/local.go b/rpc/client/local/local.go index 95f3c63b9..8b2a88314 100644 --- a/rpc/client/local/local.go +++ b/rpc/client/local/local.go @@ -198,7 +198,7 @@ func (c *Local) BlockSearch( } func (c *Local) BroadcastEvidence(ctx context.Context, ev types.Evidence) (*coretypes.ResultBroadcastEvidence, error) { - return c.env.BroadcastEvidence(ctx, ev) + return c.env.BroadcastEvidence(ctx, coretypes.Evidence{Value: ev}) } func (c *Local) Subscribe( diff --git a/rpc/client/mock/client.go b/rpc/client/mock/client.go index 05a3ca9cb..b47ff1e76 100644 --- a/rpc/client/mock/client.go +++ b/rpc/client/mock/client.go @@ -155,5 +155,5 @@ func (c Client) Validators(ctx context.Context, height *int64, page, perPage *in } func (c Client) BroadcastEvidence(ctx context.Context, ev types.Evidence) (*coretypes.ResultBroadcastEvidence, error) { - return c.env.BroadcastEvidence(ctx, ev) + return c.env.BroadcastEvidence(ctx, coretypes.Evidence{Value: ev}) } diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index 3e0e39554..5fceaef58 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/base64" + "encoding/json" "fmt" "math" "net/http" @@ -21,7 +22,6 @@ import ( "github.com/tendermint/tendermint/crypto/encoding" "github.com/tendermint/tendermint/internal/mempool" rpccore "github.com/tendermint/tendermint/internal/rpc/core" - tmjson "github.com/tendermint/tendermint/libs/json" "github.com/tendermint/tendermint/libs/log" tmmath "github.com/tendermint/tendermint/libs/math" "github.com/tendermint/tendermint/libs/service" @@ -305,7 +305,7 @@ func TestClientMethodCalls(t *testing.T) { doc := []byte(strings.Join(decoded, "")) var out types.GenesisDoc - require.NoError(t, tmjson.Unmarshal(doc, &out), + require.NoError(t, json.Unmarshal(doc, &out), "first: %+v, doc: %s", first, string(doc)) }) t.Run("ABCIQuery", func(t *testing.T) { @@ -582,7 +582,7 @@ func TestClientMethodCalls(t *testing.T) { }) t.Run("BroadcastEmpty", func(t *testing.T) { _, err := c.BroadcastEvidence(ctx, nil) - assert.Error(t, err) + require.Error(t, err) }) }) }) diff --git a/rpc/coretypes/responses.go b/rpc/coretypes/responses.go index 47649557c..2a1cf45b0 100644 --- a/rpc/coretypes/responses.go +++ b/rpc/coretypes/responses.go @@ -3,10 +3,12 @@ package coretypes import ( "encoding/json" "errors" + "fmt" "time" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/internal/jsontypes" "github.com/tendermint/tendermint/libs/bytes" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/tendermint/tendermint/types" @@ -26,7 +28,7 @@ var ( // List of blocks type ResultBlockchainInfo struct { - LastHeight int64 `json:"last_height"` + LastHeight int64 `json:"last_height,string"` BlockMetas []*types.BlockMeta `json:"block_metas"` } @@ -40,8 +42,8 @@ type ResultGenesis struct { // document to JSON and then splitting the resulting payload into // 16 megabyte blocks and then base64 encoding each block. type ResultGenesisChunk struct { - ChunkNumber int `json:"chunk"` - TotalChunks int `json:"total"` + ChunkNumber int `json:"chunk,string"` + TotalChunks int `json:"total,string"` Data string `json:"data"` } @@ -64,9 +66,9 @@ type ResultCommit struct { // ABCI results from a block type ResultBlockResults struct { - Height int64 `json:"height"` + Height int64 `json:"height,string"` TxsResults []*abci.ResponseDeliverTx `json:"txs_results"` - TotalGasUsed int64 `json:"total_gas_used"` + TotalGasUsed int64 `json:"total_gas_used,string"` BeginBlockEvents []abci.Event `json:"begin_block_events"` EndBlockEvents []abci.Event `json:"end_block_events"` ValidatorUpdates []abci.ValidatorUpdate `json:"validator_updates"` @@ -91,35 +93,64 @@ func NewResultCommit(header *types.Header, commit *types.Commit, type SyncInfo struct { LatestBlockHash bytes.HexBytes `json:"latest_block_hash"` LatestAppHash bytes.HexBytes `json:"latest_app_hash"` - LatestBlockHeight int64 `json:"latest_block_height"` + LatestBlockHeight int64 `json:"latest_block_height,string"` LatestBlockTime time.Time `json:"latest_block_time"` EarliestBlockHash bytes.HexBytes `json:"earliest_block_hash"` EarliestAppHash bytes.HexBytes `json:"earliest_app_hash"` - EarliestBlockHeight int64 `json:"earliest_block_height"` + EarliestBlockHeight int64 `json:"earliest_block_height,string"` EarliestBlockTime time.Time `json:"earliest_block_time"` - MaxPeerBlockHeight int64 `json:"max_peer_block_height"` + MaxPeerBlockHeight int64 `json:"max_peer_block_height,string"` CatchingUp bool `json:"catching_up"` - TotalSyncedTime time.Duration `json:"total_synced_time"` - RemainingTime time.Duration `json:"remaining_time"` + TotalSyncedTime time.Duration `json:"total_synced_time,string"` + RemainingTime time.Duration `json:"remaining_time,string"` - TotalSnapshots int64 `json:"total_snapshots"` - ChunkProcessAvgTime time.Duration `json:"chunk_process_avg_time"` - SnapshotHeight int64 `json:"snapshot_height"` - SnapshotChunksCount int64 `json:"snapshot_chunks_count"` - SnapshotChunksTotal int64 `json:"snapshot_chunks_total"` - BackFilledBlocks int64 `json:"backfilled_blocks"` - BackFillBlocksTotal int64 `json:"backfill_blocks_total"` + TotalSnapshots int64 `json:"total_snapshots,string"` + ChunkProcessAvgTime time.Duration `json:"chunk_process_avg_time,string"` + SnapshotHeight int64 `json:"snapshot_height,string"` + SnapshotChunksCount int64 `json:"snapshot_chunks_count,string"` + SnapshotChunksTotal int64 `json:"snapshot_chunks_total,string"` + BackFilledBlocks int64 `json:"backfilled_blocks,string"` + BackFillBlocksTotal int64 `json:"backfill_blocks_total,string"` } // Info about the node's validator type ValidatorInfo struct { - Address bytes.HexBytes `json:"address"` - PubKey crypto.PubKey `json:"pub_key"` - VotingPower int64 `json:"voting_power"` + Address bytes.HexBytes + PubKey crypto.PubKey + VotingPower int64 +} + +type validatorInfoJSON struct { + Address bytes.HexBytes `json:"address"` + PubKey json.RawMessage `json:"pub_key"` + VotingPower int64 `json:"voting_power,string"` +} + +func (v ValidatorInfo) MarshalJSON() ([]byte, error) { + pk, err := jsontypes.Marshal(v.PubKey) + if err != nil { + return nil, err + } + return json.Marshal(validatorInfoJSON{ + Address: v.Address, PubKey: pk, VotingPower: v.VotingPower, + }) +} + +func (v *ValidatorInfo) UnmarshalJSON(data []byte) error { + var val validatorInfoJSON + if err := json.Unmarshal(data, &val); err != nil { + return err + } + if err := jsontypes.Unmarshal(val.PubKey, &v.PubKey); err != nil { + return err + } + v.Address = val.Address + v.VotingPower = val.VotingPower + return nil } // Node Status @@ -142,7 +173,7 @@ func (s *ResultStatus) TxIndexEnabled() bool { type ResultNetInfo struct { Listening bool `json:"listening"` Listeners []string `json:"listeners"` - NPeers int `json:"n_peers"` + NPeers int `json:"n_peers,string"` Peers []Peer `json:"peers"` } @@ -164,12 +195,11 @@ type Peer struct { // Validators for a height. type ResultValidators struct { - BlockHeight int64 `json:"block_height"` + BlockHeight int64 `json:"block_height,string"` Validators []*types.Validator `json:"validators"` - // Count of actual validators in this result - Count int `json:"count"` - // Total number of validators - Total int `json:"total"` + + Count int `json:"count,string"` // Count of actual validators in this result + Total int `json:"total,string"` // Total number of validators } // ConsensusParams for given height @@ -203,8 +233,7 @@ type ResultBroadcastTx struct { Log string `json:"log"` Codespace string `json:"codespace"` MempoolError string `json:"mempool_error"` - - Hash bytes.HexBytes `json:"hash"` + Hash bytes.HexBytes `json:"hash"` } // CheckTx and DeliverTx results @@ -212,7 +241,7 @@ type ResultBroadcastTxCommit struct { CheckTx abci.ResponseCheckTx `json:"check_tx"` DeliverTx abci.ResponseDeliverTx `json:"deliver_tx"` Hash bytes.HexBytes `json:"hash"` - Height int64 `json:"height"` + Height int64 `json:"height,string"` } // ResultCheckTx wraps abci.ResponseCheckTx. @@ -223,7 +252,7 @@ type ResultCheckTx struct { // Result of querying for a tx type ResultTx struct { Hash bytes.HexBytes `json:"hash"` - Height int64 `json:"height"` + Height int64 `json:"height,string"` Index uint32 `json:"index"` TxResult abci.ResponseDeliverTx `json:"tx_result"` Tx types.Tx `json:"tx"` @@ -233,20 +262,20 @@ type ResultTx struct { // Result of searching for txs type ResultTxSearch struct { Txs []*ResultTx `json:"txs"` - TotalCount int `json:"total_count"` + TotalCount int `json:"total_count,string"` } // ResultBlockSearch defines the RPC response type for a block search by events. type ResultBlockSearch struct { Blocks []*ResultBlock `json:"blocks"` - TotalCount int `json:"total_count"` + TotalCount int `json:"total_count,string"` } // List of mempool txs type ResultUnconfirmedTxs struct { - Count int `json:"n_txs"` - Total int `json:"total"` - TotalBytes int64 `json:"total_bytes"` + Count int `json:"n_txs,string"` + Total int `json:"total,string"` + TotalBytes int64 `json:"total_bytes,string"` Txs []types.Tx `json:"txs"` } @@ -276,8 +305,55 @@ type ( // Event data from a subscription type ResultEvent struct { - SubscriptionID string `json:"subscription_id"` - Query string `json:"query"` - Data types.TMEventData `json:"data"` - Events []abci.Event `json:"events"` + SubscriptionID string + Query string + Data types.TMEventData + Events []abci.Event } + +type resultEventJSON struct { + SubscriptionID string `json:"subscription_id"` + Query string `json:"query"` + Data json.RawMessage `json:"data"` + Events []abci.Event `json:"events"` +} + +func (r ResultEvent) MarshalJSON() ([]byte, error) { + data, ok := r.Data.(jsontypes.Tagged) + if !ok { + return nil, fmt.Errorf("type %T is not tagged", r.Data) + } + evt, err := jsontypes.Marshal(data) + if err != nil { + return nil, err + } + return json.Marshal(resultEventJSON{ + SubscriptionID: r.SubscriptionID, + Query: r.Query, + Data: evt, + Events: r.Events, + }) +} + +func (r *ResultEvent) UnmarshalJSON(data []byte) error { + var res resultEventJSON + if err := json.Unmarshal(data, &res); err != nil { + return err + } + if err := jsontypes.Unmarshal(res.Data, &r.Data); err != nil { + return err + } + r.SubscriptionID = res.SubscriptionID + r.Query = res.Query + r.Events = res.Events + return nil +} + +// Evidence is an argument wrapper for a types.Evidence value, that handles +// encoding and decoding through JSON. +type Evidence struct { + Value types.Evidence +} + +func (e Evidence) MarshalJSON() ([]byte, error) { return jsontypes.Marshal(e.Value) } +func (e *Evidence) UnmarshalJSON(data []byte) error { return jsontypes.Unmarshal(data, &e.Value) } diff --git a/rpc/jsonrpc/client/decode.go b/rpc/jsonrpc/client/decode.go index 5d41a94c0..2f5ec5fcc 100644 --- a/rpc/jsonrpc/client/decode.go +++ b/rpc/jsonrpc/client/decode.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" - tmjson "github.com/tendermint/tendermint/libs/json" rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types" ) @@ -26,7 +25,7 @@ func unmarshalResponseBytes(responseBytes []byte, expectedID rpctypes.JSONRPCInt } // Unmarshal the RawMessage into the result. - if err := tmjson.Unmarshal(response.Result, result); err != nil { + if err := json.Unmarshal(response.Result, result); err != nil { return fmt.Errorf("error unmarshaling result: %w", err) } return nil @@ -71,7 +70,7 @@ func unmarshalResponseBytesArray( } for i := 0; i < len(responses); i++ { - if err := tmjson.Unmarshal(responses[i].Result, results[i]); err != nil { + if err := json.Unmarshal(responses[i].Result, results[i]); err != nil { return nil, fmt.Errorf("error unmarshaling #%d result: %w", i, err) } } diff --git a/rpc/jsonrpc/server/http_json_handler.go b/rpc/jsonrpc/server/http_json_handler.go index 035793d93..e41274a45 100644 --- a/rpc/jsonrpc/server/http_json_handler.go +++ b/rpc/jsonrpc/server/http_json_handler.go @@ -2,6 +2,7 @@ package server import ( "bytes" + "context" "encoding/json" "errors" "fmt" @@ -9,8 +10,8 @@ import ( "net/http" "reflect" "sort" + "strconv" - tmjson "github.com/tendermint/tendermint/libs/json" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/rpc/coretypes" rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types" @@ -63,7 +64,12 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han continue } - args, err := parseParams(rpcFunc, hreq, req) + req := req + ctx := rpctypes.WithCallInfo(hreq.Context(), &rpctypes.CallInfo{ + RPCRequest: &req, + HTTPRequest: hreq, + }) + args, err := parseParams(ctx, rpcFunc, req.Params) if err != nil { responses = append(responses, rpctypes.RPCInvalidParamsError( req.ID, fmt.Errorf("converting JSON parameters: %w", err))) @@ -132,99 +138,101 @@ func parseRequests(data []byte) ([]rpctypes.RPCRequest, error) { return reqs, nil } -func mapParamsToArgs( - rpcFunc *RPCFunc, - params map[string]json.RawMessage, - argsOffset int, -) ([]reflect.Value, error) { +// parseParams parses the JSON parameters of rpcReq into the arguments of fn, +// returning the corresponding argument values or an error. +func parseParams(ctx context.Context, fn *RPCFunc, paramData []byte) ([]reflect.Value, error) { + params, err := parseJSONParams(fn, paramData) + if err != nil { + return nil, err + } - values := make([]reflect.Value, len(rpcFunc.argNames)) - for i, argName := range rpcFunc.argNames { - argType := rpcFunc.args[i+argsOffset] + args := make([]reflect.Value, 1+len(params)) + args[0] = reflect.ValueOf(ctx) + for i, param := range params { + ptype := fn.args[i+1] + if len(param) == 0 { + args[i+1] = reflect.Zero(ptype) + continue + } - if p, ok := params[argName]; ok && p != nil && len(p) > 0 { - val := reflect.New(argType) - err := tmjson.Unmarshal(p, val.Interface()) - if err != nil { - return nil, err + var pval reflect.Value + isPtr := ptype.Kind() == reflect.Ptr + if isPtr { + pval = reflect.New(ptype.Elem()) + } else { + pval = reflect.New(ptype) + } + baseType := pval.Type().Elem() + + if isIntType(baseType) && isStringValue(param) { + var z int64String + if err := json.Unmarshal(param, &z); err != nil { + return nil, fmt.Errorf("decoding string %q: %w", fn.argNames[i], err) } - values[i] = val.Elem() - } else { // use default for that type - values[i] = reflect.Zero(argType) + pval.Elem().Set(reflect.ValueOf(z).Convert(baseType)) + } else if err := json.Unmarshal(param, pval.Interface()); err != nil { + return nil, fmt.Errorf("decoding %q: %w", fn.argNames[i], err) } - } - return values, nil + if isPtr { + args[i+1] = pval + } else { + args[i+1] = pval.Elem() + } + } + return args, nil } -func arrayParamsToArgs( - rpcFunc *RPCFunc, - params []json.RawMessage, - argsOffset int, -) ([]reflect.Value, error) { - - if len(rpcFunc.argNames) != len(params) { - return nil, fmt.Errorf("expected %v parameters (%v), got %v (%v)", - len(rpcFunc.argNames), rpcFunc.argNames, len(params), params) - } +// parseJSONParams parses data and returns a slice of JSON values matching the +// positional parameters of fn. It reports an error if data is not "null" and +// does not encode an object or an array, or if the number of array parameters +// does not match the argument list of fn (excluding the context). +func parseJSONParams(fn *RPCFunc, data []byte) ([]json.RawMessage, error) { + base := bytes.TrimSpace(data) + if bytes.HasPrefix(base, []byte("{")) { + var m map[string]json.RawMessage + if err := json.Unmarshal(base, &m); err != nil { + return nil, fmt.Errorf("decoding parameter object: %w", err) + } + out := make([]json.RawMessage, len(fn.argNames)) + for i, name := range fn.argNames { + if p, ok := m[name]; ok { + out[i] = p + } + } + return out, nil - values := make([]reflect.Value, len(params)) - for i, p := range params { - argType := rpcFunc.args[i+argsOffset] - val := reflect.New(argType) - err := tmjson.Unmarshal(p, val.Interface()) - if err != nil { - return nil, err + } else if bytes.HasPrefix(base, []byte("[")) { + var m []json.RawMessage + if err := json.Unmarshal(base, &m); err != nil { + return nil, fmt.Errorf("decoding parameter array: %w", err) } - values[i] = val.Elem() + if len(m) != len(fn.argNames) { + return nil, fmt.Errorf("got %d parameters, want %d", len(m), len(fn.argNames)) + } + return m, nil + + } else if bytes.Equal(base, []byte("null")) { + return make([]json.RawMessage, len(fn.argNames)), nil } - return values, nil + + return nil, errors.New("parameters must be an object or an array") } -// parseParams parses the JSON parameters of rpcReq into the arguments of fn, -// returning the corresponding argument values or an error. -func parseParams(fn *RPCFunc, httpReq *http.Request, rpcReq rpctypes.RPCRequest) ([]reflect.Value, error) { - ctx := rpctypes.WithCallInfo(httpReq.Context(), &rpctypes.CallInfo{ - RPCRequest: &rpcReq, - HTTPRequest: httpReq, - }) - args := []reflect.Value{reflect.ValueOf(ctx)} - if len(rpcReq.Params) == 0 { - return args, nil - } - fargs, err := jsonParamsToArgs(fn, rpcReq.Params) - if err != nil { - return nil, err - } - return append(args, fargs...), nil +// isStringValue reports whether data is a JSON string value. +func isStringValue(data json.RawMessage) bool { + return len(data) != 0 && data[0] == '"' } -// raw is unparsed json (from json.RawMessage) encoding either a map or an -// array. -// -// Example: -// rpcFunc.args = [context.Context string] -// rpcFunc.argNames = ["arg"] -func jsonParamsToArgs(rpcFunc *RPCFunc, raw []byte) ([]reflect.Value, error) { - const argsOffset = 1 - - // TODO: Make more efficient, perhaps by checking the first character for '{' or '['? - // 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) - } +type int64String int64 - // Otherwise, try an array. - var a []json.RawMessage - err = json.Unmarshal(raw, &a) - if err == nil { - return arrayParamsToArgs(rpcFunc, a, argsOffset) +func (z *int64String) UnmarshalText(data []byte) error { + v, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return err } - - // Otherwise, bad format, we cannot parse - return nil, fmt.Errorf("unknown type for JSON params: %v. Expected map or array", err) + *z = int64String(v) + return nil } // writes a list of available rpc endpoints as an html page diff --git a/rpc/jsonrpc/server/http_json_handler_test.go b/rpc/jsonrpc/server/http_json_handler_test.go index 15aa8c4c1..2d56ef072 100644 --- a/rpc/jsonrpc/server/http_json_handler_test.go +++ b/rpc/jsonrpc/server/http_json_handler_test.go @@ -46,7 +46,7 @@ func TestRPCParams(t *testing.T) { // id not captured in JSON parsing failures {`{"method": "c", "id": "0", "params": a}`, "invalid character", nil}, {`{"method": "c", "id": "0", "params": ["a"]}`, "got 1", rpctypes.JSONRPCStringID("0")}, - {`{"method": "c", "id": "0", "params": ["a", "b"]}`, "invalid character", rpctypes.JSONRPCStringID("0")}, + {`{"method": "c", "id": "0", "params": ["a", "b"]}`, "invalid syntax", rpctypes.JSONRPCStringID("0")}, {`{"method": "c", "id": "0", "params": [1, 1]}`, "of type string", rpctypes.JSONRPCStringID("0")}, // no ID - notification diff --git a/rpc/jsonrpc/server/parse_test.go b/rpc/jsonrpc/server/parse_test.go index 9b222b507..ee0ab5d79 100644 --- a/rpc/jsonrpc/server/parse_test.go +++ b/rpc/jsonrpc/server/parse_test.go @@ -153,17 +153,17 @@ func TestParseJSONRPC(t *testing.T) { {`[7,"flew",100]`, 0, "", true}, {`{"name": -12, "height": "fred"}`, 0, "", true}, } + ctx := context.Background() for idx, tc := range cases { i := strconv.Itoa(idx) - data := []byte(tc.raw) - vals, err := jsonParamsToArgs(call, data) + vals, err := parseParams(ctx, call, []byte(tc.raw)) if tc.fail { assert.Error(t, err, i) } else { assert.NoError(t, err, "%s: %+v", i, err) - if assert.Equal(t, 2, len(vals), i) { - assert.Equal(t, tc.height, vals[0].Int(), i) - assert.Equal(t, tc.name, vals[1].String(), i) + if assert.Equal(t, 3, len(vals), i) { // ctx, height, name + assert.Equal(t, tc.height, vals[1].Int(), i) + assert.Equal(t, tc.name, vals[2].String(), i) } } diff --git a/rpc/jsonrpc/server/ws_handler.go b/rpc/jsonrpc/server/ws_handler.go index 818e66512..d3051903d 100644 --- a/rpc/jsonrpc/server/ws_handler.go +++ b/rpc/jsonrpc/server/ws_handler.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "net/http" - "reflect" "runtime/debug" "time" @@ -371,18 +370,14 @@ func (wsc *wsConnection) readRoutine(ctx context.Context) { RPCRequest: &request, WSConn: wsc, }) - args := []reflect.Value{reflect.ValueOf(fctx)} - if len(request.Params) > 0 { - fnArgs, err := jsonParamsToArgs(rpcFunc, request.Params) - if err != nil { - if err := wsc.WriteRPCResponse(writeCtx, - rpctypes.RPCInvalidParamsError(request.ID, fmt.Errorf("error converting json params to arguments: %w", err)), - ); err != nil { - wsc.Logger.Error("error writing RPC response", "err", err) - } - continue + args, err := parseParams(fctx, rpcFunc, request.Params) + if err != nil { + if err := wsc.WriteRPCResponse(writeCtx, rpctypes.RPCInvalidParamsError( + request.ID, fmt.Errorf("error converting json params to arguments: %w", err)), + ); err != nil { + wsc.Logger.Error("error writing RPC response", "err", err) } - args = append(args, fnArgs...) + continue } returns := rpcFunc.f.Call(args) diff --git a/rpc/jsonrpc/types/types.go b/rpc/jsonrpc/types/types.go index d13c2b842..108d056a7 100644 --- a/rpc/jsonrpc/types/types.go +++ b/rpc/jsonrpc/types/types.go @@ -7,8 +7,6 @@ import ( "net/http" "reflect" "strings" - - tmjson "github.com/tendermint/tendermint/libs/json" ) // a wrapper to emulate a sum type: jsonrpcid = string | int @@ -100,29 +98,10 @@ func (req RPCRequest) String() string { // ParamsToRequest constructs a new RPCRequest with the given ID, method, and parameters. func ParamsToRequest(id jsonrpcid, method string, params interface{}) (RPCRequest, error) { - var payload json.RawMessage - var err error - switch t := params.(type) { - case map[string]interface{}: - // TODO(creachadair): This special case preserves existing behavior that - // relies on the custom JSON encoding library. Remove it once that - // requirement has been removed. - paramsMap := make(map[string]json.RawMessage, len(t)) - for name, value := range t { - valueJSON, err := tmjson.Marshal(value) - if err != nil { - return RPCRequest{}, err - } - paramsMap[name] = valueJSON - } - payload, err = json.Marshal(paramsMap) - default: - payload, err = json.Marshal(params) - } + payload, err := json.Marshal(params) if err != nil { return RPCRequest{}, err } - return NewRPCRequest(id, method, payload), nil } @@ -162,6 +141,7 @@ func (resp *RPCResponse) UnmarshalJSON(data []byte) error { if err != nil { return err } + resp.JSONRPC = unsafeResp.JSONRPC resp.Error = unsafeResp.Error resp.Result = unsafeResp.Result @@ -177,18 +157,11 @@ func (resp *RPCResponse) UnmarshalJSON(data []byte) error { } func NewRPCSuccessResponse(id jsonrpcid, res interface{}) RPCResponse { - var rawMsg json.RawMessage - - if res != nil { - var js []byte - js, err := tmjson.Marshal(res) - if err != nil { - return RPCInternalError(id, fmt.Errorf("error marshaling response: %w", err)) - } - rawMsg = json.RawMessage(js) + result, err := json.Marshal(res) + if err != nil { + return RPCInternalError(id, fmt.Errorf("error marshaling response: %w", err)) } - - return RPCResponse{JSONRPC: "2.0", ID: id, Result: rawMsg} + return RPCResponse{JSONRPC: "2.0", ID: id, Result: result} } func NewRPCErrorResponse(id jsonrpcid, code int, msg string, data string) RPCResponse { diff --git a/test/e2e/runner/evidence.go b/test/e2e/runner/evidence.go index a81094ad7..8f8408ff1 100644 --- a/test/e2e/runner/evidence.go +++ b/test/e2e/runner/evidence.go @@ -3,6 +3,7 @@ package main import ( "bytes" "context" + "encoding/json" "errors" "fmt" "math/rand" @@ -13,7 +14,6 @@ import ( "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/tmhash" "github.com/tendermint/tendermint/internal/test/factory" - tmjson "github.com/tendermint/tendermint/libs/json" "github.com/tendermint/tendermint/privval" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" e2e "github.com/tendermint/tendermint/test/e2e/pkg" @@ -241,7 +241,7 @@ func readPrivKey(keyFilePath string) (crypto.PrivKey, error) { return nil, err } pvKey := privval.FilePVKey{} - err = tmjson.Unmarshal(keyJSONBytes, &pvKey) + err = json.Unmarshal(keyJSONBytes, &pvKey) if err != nil { return nil, fmt.Errorf("error reading PrivValidator key from %v: %w", keyFilePath, err) } diff --git a/types/events.go b/types/events.go index 00bed4b60..7a4d7d543 100644 --- a/types/events.go +++ b/types/events.go @@ -6,9 +6,9 @@ import ( "strings" abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/internal/jsontypes" tmpubsub "github.com/tendermint/tendermint/internal/pubsub" tmquery "github.com/tendermint/tendermint/internal/pubsub/query" - tmjson "github.com/tendermint/tendermint/libs/json" ) // Reserved event types (alphabetically sorted). @@ -90,23 +90,21 @@ var ( // ENCODING / DECODING // TMEventData implements events.EventData. -type TMEventData interface { - // empty interface -} +type TMEventData interface{} func init() { - tmjson.RegisterType(EventDataNewBlock{}, "tendermint/event/NewBlock") - tmjson.RegisterType(EventDataNewBlockHeader{}, "tendermint/event/NewBlockHeader") - tmjson.RegisterType(EventDataNewEvidence{}, "tendermint/event/NewEvidence") - tmjson.RegisterType(EventDataTx{}, "tendermint/event/Tx") - tmjson.RegisterType(EventDataRoundState{}, "tendermint/event/RoundState") - tmjson.RegisterType(EventDataNewRound{}, "tendermint/event/NewRound") - tmjson.RegisterType(EventDataCompleteProposal{}, "tendermint/event/CompleteProposal") - tmjson.RegisterType(EventDataVote{}, "tendermint/event/Vote") - tmjson.RegisterType(EventDataValidatorSetUpdates{}, "tendermint/event/ValidatorSetUpdates") - tmjson.RegisterType(EventDataString(""), "tendermint/event/ProposalString") - tmjson.RegisterType(EventDataBlockSyncStatus{}, "tendermint/event/FastSyncStatus") - tmjson.RegisterType(EventDataStateSyncStatus{}, "tendermint/event/StateSyncStatus") + jsontypes.MustRegister(EventDataBlockSyncStatus{}) + jsontypes.MustRegister(EventDataCompleteProposal{}) + jsontypes.MustRegister(EventDataNewBlock{}) + jsontypes.MustRegister(EventDataNewBlockHeader{}) + jsontypes.MustRegister(EventDataNewEvidence{}) + jsontypes.MustRegister(EventDataNewRound{}) + jsontypes.MustRegister(EventDataRoundState{}) + jsontypes.MustRegister(EventDataStateSyncStatus{}) + jsontypes.MustRegister(EventDataTx{}) + jsontypes.MustRegister(EventDataValidatorSetUpdates{}) + jsontypes.MustRegister(EventDataVote{}) + jsontypes.MustRegister(EventDataString("")) } // Most event messages are basic types (a block, a transaction) @@ -120,6 +118,9 @@ type EventDataNewBlock struct { ResultEndBlock abci.ResponseEndBlock `json:"result_end_block"` } +// TypeTag implements the required method of jsontypes.Tagged. +func (EventDataNewBlock) TypeTag() string { return "tendermint/event/NewBlock" } + type EventDataNewBlockHeader struct { Header Header `json:"header"` @@ -128,17 +129,26 @@ type EventDataNewBlockHeader struct { ResultEndBlock abci.ResponseEndBlock `json:"result_end_block"` } +// TypeTag implements the required method of jsontypes.Tagged. +func (EventDataNewBlockHeader) TypeTag() string { return "tendermint/event/NewBlockHeader" } + type EventDataNewEvidence struct { Evidence Evidence `json:"evidence"` Height int64 `json:"height"` } +// TypeTag implements the required method of jsontypes.Tagged. +func (EventDataNewEvidence) TypeTag() string { return "tendermint/event/NewEvidence" } + // All txs fire EventDataTx type EventDataTx struct { abci.TxResult } +// TypeTag implements the required method of jsontypes.Tagged. +func (EventDataTx) TypeTag() string { return "tendermint/event/Tx" } + // NOTE: This goes into the replay WAL type EventDataRoundState struct { Height int64 `json:"height"` @@ -146,6 +156,9 @@ type EventDataRoundState struct { Step string `json:"step"` } +// TypeTag implements the required method of jsontypes.Tagged. +func (EventDataRoundState) TypeTag() string { return "tendermint/event/RoundState" } + type ValidatorInfo struct { Address Address `json:"address"` Index int32 `json:"index"` @@ -159,6 +172,9 @@ type EventDataNewRound struct { Proposer ValidatorInfo `json:"proposer"` } +// TypeTag implements the required method of jsontypes.Tagged. +func (EventDataNewRound) TypeTag() string { return "tendermint/event/NewRound" } + type EventDataCompleteProposal struct { Height int64 `json:"height"` Round int32 `json:"round"` @@ -167,16 +183,28 @@ type EventDataCompleteProposal struct { BlockID BlockID `json:"block_id"` } +// TypeTag implements the required method of jsontypes.Tagged. +func (EventDataCompleteProposal) TypeTag() string { return "tendermint/event/CompleteProposal" } + type EventDataVote struct { Vote *Vote } +// TypeTag implements the required method of jsontypes.Tagged. +func (EventDataVote) TypeTag() string { return "tendermint/event/Vote" } + type EventDataString string +// TypeTag implements the required method of jsontypes.Tagged. +func (EventDataString) TypeTag() string { return "tendermint/event/ProposalString" } + type EventDataValidatorSetUpdates struct { ValidatorUpdates []*Validator `json:"validator_updates"` } +// TypeTag implements the required method of jsontypes.Tagged. +func (EventDataValidatorSetUpdates) TypeTag() string { return "tendermint/event/ValidatorSetUpdates" } + // EventDataBlockSyncStatus shows the fastsync status and the // height when the node state sync mechanism changes. type EventDataBlockSyncStatus struct { @@ -184,6 +212,9 @@ type EventDataBlockSyncStatus struct { Height int64 `json:"height"` } +// TypeTag implements the required method of jsontypes.Tagged. +func (EventDataBlockSyncStatus) TypeTag() string { return "tendermint/event/FastSyncStatus" } + // EventDataStateSyncStatus shows the statesync status and the // height when the node state sync mechanism changes. type EventDataStateSyncStatus struct { @@ -191,6 +222,9 @@ type EventDataStateSyncStatus struct { Height int64 `json:"height"` } +// TypeTag implements the required method of jsontypes.Tagged. +func (EventDataStateSyncStatus) TypeTag() string { return "tendermint/event/StateSyncStatus" } + // PUBSUB const ( diff --git a/types/evidence.go b/types/evidence.go index cca6fc899..c9a60786a 100644 --- a/types/evidence.go +++ b/types/evidence.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/binary" + "encoding/json" "errors" "fmt" "sort" @@ -14,7 +15,6 @@ import ( "github.com/tendermint/tendermint/crypto/merkle" "github.com/tendermint/tendermint/crypto/tmhash" "github.com/tendermint/tendermint/internal/jsontypes" - tmjson "github.com/tendermint/tendermint/libs/json" tmrand "github.com/tendermint/tendermint/libs/rand" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" ) @@ -554,6 +554,33 @@ func LightClientAttackEvidenceFromProto(lpb *tmproto.LightClientAttackEvidence) // EvidenceList is a list of Evidence. Evidences is not a word. type EvidenceList []Evidence +func (evl EvidenceList) MarshalJSON() ([]byte, error) { + lst := make([]json.RawMessage, len(evl)) + for i, ev := range evl { + bits, err := jsontypes.Marshal(ev) + if err != nil { + return nil, err + } + lst[i] = bits + } + return json.Marshal(lst) +} + +func (evl *EvidenceList) UnmarshalJSON(data []byte) error { + var lst []json.RawMessage + if err := json.Unmarshal(data, &lst); err != nil { + return err + } + out := make([]Evidence, len(lst)) + for i, elt := range lst { + if err := jsontypes.Unmarshal(elt, &out[i]); err != nil { + return err + } + } + *evl = EvidenceList(out) + return nil +} + // Hash returns the simple merkle root hash of the EvidenceList. func (evl EvidenceList) Hash() []byte { // These allocations are required because Evidence is not of type Bytes, and @@ -638,9 +665,6 @@ func EvidenceFromProto(evidence *tmproto.Evidence) (Evidence, error) { } func init() { - tmjson.RegisterType(&DuplicateVoteEvidence{}, "tendermint/DuplicateVoteEvidence") - tmjson.RegisterType(&LightClientAttackEvidence{}, "tendermint/LightClientAttackEvidence") - jsontypes.MustRegister((*DuplicateVoteEvidence)(nil)) jsontypes.MustRegister((*LightClientAttackEvidence)(nil)) } diff --git a/types/genesis.go b/types/genesis.go index d89de6ea2..f711a7090 100644 --- a/types/genesis.go +++ b/types/genesis.go @@ -11,7 +11,6 @@ import ( "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/internal/jsontypes" tmbytes "github.com/tendermint/tendermint/libs/bytes" - tmjson "github.com/tendermint/tendermint/libs/json" tmtime "github.com/tendermint/tendermint/libs/time" ) @@ -28,10 +27,17 @@ const ( // GenesisValidator is an initial validator. type GenesisValidator struct { - Address Address `json:"address"` - PubKey crypto.PubKey `json:"pub_key"` - Power int64 `json:"power,string"` - Name string `json:"name"` + Address Address + PubKey crypto.PubKey + Power int64 + Name string +} + +type genesisValidatorJSON struct { + Address Address `json:"address"` + PubKey json.RawMessage `json:"pub_key"` + Power int64 `json:"power,string"` + Name string `json:"name"` } func (g GenesisValidator) MarshalJSON() ([]byte, error) { @@ -39,19 +45,30 @@ func (g GenesisValidator) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - return json.Marshal(struct { - Address Address `json:"address"` - PubKey json.RawMessage `json:"pub_key"` - Power int64 `json:"power,string"` - Name string `json:"name"` - }{Address: g.Address, PubKey: pk, Power: g.Power, Name: g.Name}) + return json.Marshal(genesisValidatorJSON{ + Address: g.Address, PubKey: pk, Power: g.Power, Name: g.Name, + }) +} + +func (g *GenesisValidator) UnmarshalJSON(data []byte) error { + var gv genesisValidatorJSON + if err := json.Unmarshal(data, &gv); err != nil { + return err + } + if err := jsontypes.Unmarshal(gv.PubKey, &g.PubKey); err != nil { + return err + } + g.Address = gv.Address + g.Power = gv.Power + g.Name = gv.Name + return nil } // GenesisDoc defines the initial conditions for a tendermint blockchain, in particular its validator set. type GenesisDoc struct { GenesisTime time.Time `json:"genesis_time"` ChainID string `json:"chain_id"` - InitialHeight int64 `json:"initial_height"` + InitialHeight int64 `json:"initial_height,string"` ConsensusParams *ConsensusParams `json:"consensus_params,omitempty"` Validators []GenesisValidator `json:"validators,omitempty"` AppHash tmbytes.HexBytes `json:"app_hash"` @@ -60,7 +77,7 @@ type GenesisDoc struct { // SaveAs is a utility method for saving GenensisDoc as a JSON file. func (genDoc *GenesisDoc) SaveAs(file string) error { - genDocBytes, err := tmjson.MarshalIndent(genDoc, "", " ") + genDocBytes, err := json.MarshalIndent(genDoc, "", " ") if err != nil { return err } @@ -125,7 +142,7 @@ func (genDoc *GenesisDoc) ValidateAndComplete() error { // GenesisDocFromJSON unmarshalls JSON data into a GenesisDoc. func GenesisDocFromJSON(jsonBlob []byte) (*GenesisDoc, error) { genDoc := GenesisDoc{} - err := tmjson.Unmarshal(jsonBlob, &genDoc) + err := json.Unmarshal(jsonBlob, &genDoc) if err != nil { return nil, err } diff --git a/types/genesis_test.go b/types/genesis_test.go index 422c2125f..a82ce46ea 100644 --- a/types/genesis_test.go +++ b/types/genesis_test.go @@ -1,6 +1,7 @@ package types import ( + "encoding/json" "os" "testing" @@ -8,7 +9,6 @@ import ( "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto/ed25519" - tmjson "github.com/tendermint/tendermint/libs/json" tmtime "github.com/tendermint/tendermint/libs/time" ) @@ -82,7 +82,7 @@ func TestGenesisGood(t *testing.T) { ChainID: "abc", Validators: []GenesisValidator{{pubkey.Address(), pubkey, 10, "myval"}}, } - genDocBytes, err = tmjson.Marshal(baseGenDoc) + genDocBytes, err = json.Marshal(baseGenDoc) assert.NoError(t, err, "error marshaling genDoc") // test base gendoc and check consensus params were filled @@ -94,14 +94,14 @@ func TestGenesisGood(t *testing.T) { assert.NotNil(t, genDoc.Validators[0].Address, "expected validator's address to be filled in") // create json with consensus params filled - genDocBytes, err = tmjson.Marshal(genDoc) + genDocBytes, err = json.Marshal(genDoc) assert.NoError(t, err, "error marshaling genDoc") genDoc, err = GenesisDocFromJSON(genDocBytes) assert.NoError(t, err, "expected no error for valid genDoc json") // test with invalid consensus params genDoc.ConsensusParams.Block.MaxBytes = 0 - genDocBytes, err = tmjson.Marshal(genDoc) + genDocBytes, err = json.Marshal(genDoc) assert.NoError(t, err, "error marshaling genDoc") _, err = GenesisDocFromJSON(genDocBytes) assert.Error(t, err, "expected error for genDoc json with block size of 0") diff --git a/types/node_info.go b/types/node_info.go index 902ca759b..57aced054 100644 --- a/types/node_info.go +++ b/types/node_info.go @@ -24,9 +24,9 @@ func MaxNodeInfoSize() int { // ProtocolVersion contains the protocol versions for the software. type ProtocolVersion struct { - P2P uint64 `json:"p2p"` - Block uint64 `json:"block"` - App uint64 `json:"app"` + P2P uint64 `json:"p2p,string"` + Block uint64 `json:"block,string"` + App uint64 `json:"app,string"` } //------------------------------------------------------------- diff --git a/types/node_key.go b/types/node_key.go index 8f59b6085..927e17065 100644 --- a/types/node_key.go +++ b/types/node_key.go @@ -7,7 +7,6 @@ import ( "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/internal/jsontypes" - tmjson "github.com/tendermint/tendermint/libs/json" tmos "github.com/tendermint/tendermint/libs/os" ) @@ -19,9 +18,14 @@ import ( // It contains the nodes private key for authentication. type NodeKey struct { // Canonical ID - hex-encoded pubkey's address (IDByteLength bytes) - ID NodeID `json:"id"` + ID NodeID // Private key - PrivKey crypto.PrivKey `json:"priv_key"` + PrivKey crypto.PrivKey +} + +type nodeKeyJSON struct { + ID NodeID `json:"id"` + PrivKey json.RawMessage `json:"priv_key"` } func (nk NodeKey) MarshalJSON() ([]byte, error) { @@ -29,10 +33,22 @@ func (nk NodeKey) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - return json.Marshal(struct { - ID NodeID `json:"id"` - PrivKey json.RawMessage `json:"priv_key"` - }{ID: nk.ID, PrivKey: pk}) + return json.Marshal(nodeKeyJSON{ + ID: nk.ID, PrivKey: pk, + }) +} + +func (nk *NodeKey) UnmarshalJSON(data []byte) error { + var nkjson nodeKeyJSON + if err := json.Unmarshal(data, &nkjson); err != nil { + return err + } + var pk crypto.PrivKey + if err := jsontypes.Unmarshal(nkjson.PrivKey, &pk); err != nil { + return err + } + *nk = NodeKey{ID: nkjson.ID, PrivKey: pk} + return nil } // PubKey returns the peer's PubKey @@ -42,7 +58,7 @@ func (nk NodeKey) PubKey() crypto.PubKey { // SaveAs persists the NodeKey to filePath. func (nk NodeKey) SaveAs(filePath string) error { - jsonBytes, err := tmjson.Marshal(nk) + jsonBytes, err := json.Marshal(nk) if err != nil { return err } @@ -85,7 +101,7 @@ func LoadNodeKey(filePath string) (NodeKey, error) { return NodeKey{}, err } nodeKey := NodeKey{} - err = tmjson.Unmarshal(jsonBytes, &nodeKey) + err = json.Unmarshal(jsonBytes, &nodeKey) if err != nil { return NodeKey{}, err } diff --git a/types/params.go b/types/params.go index 32d0f71c8..5ba065edf 100644 --- a/types/params.go +++ b/types/params.go @@ -54,15 +54,15 @@ type HashedParams struct { // BlockParams define limits on the block size and gas plus minimum time // between blocks. type BlockParams struct { - MaxBytes int64 `json:"max_bytes"` - MaxGas int64 `json:"max_gas"` + MaxBytes int64 `json:"max_bytes,string"` + MaxGas int64 `json:"max_gas,string"` } // EvidenceParams determine how we handle evidence of malfeasance. type EvidenceParams struct { - MaxAgeNumBlocks int64 `json:"max_age_num_blocks"` // only accept new evidence more recent than this - MaxAgeDuration time.Duration `json:"max_age_duration"` - MaxBytes int64 `json:"max_bytes"` + MaxAgeNumBlocks int64 `json:"max_age_num_blocks,string"` // only accept new evidence more recent than this + MaxAgeDuration time.Duration `json:"max_age_duration,string"` + MaxBytes int64 `json:"max_bytes,string"` } // ValidatorParams restrict the public key types validators can use. @@ -72,7 +72,7 @@ type ValidatorParams struct { } type VersionParams struct { - AppVersion uint64 `json:"app_version"` + AppVersion uint64 `json:"app_version,string"` } // DefaultConsensusParams returns a default ConsensusParams. diff --git a/types/validator.go b/types/validator.go index 9d48c93e7..7504e2125 100644 --- a/types/validator.go +++ b/types/validator.go @@ -17,23 +17,47 @@ import ( // NOTE: The ProposerPriority is not included in Validator.Hash(); // make sure to update that method if changes are made here type Validator struct { - Address Address `json:"address"` - PubKey crypto.PubKey `json:"pub_key"` - VotingPower int64 `json:"voting_power,string"` - ProposerPriority int64 `json:"proposer_priority,string"` + Address Address + PubKey crypto.PubKey + VotingPower int64 + ProposerPriority int64 +} + +type validatorJSON struct { + Address Address `json:"address"` + PubKey json.RawMessage `json:"pub_key,omitempty"` + VotingPower int64 `json:"voting_power,string"` + ProposerPriority int64 `json:"proposer_priority,string"` } func (v Validator) MarshalJSON() ([]byte, error) { - pk, err := jsontypes.Marshal(v.PubKey) - if err != nil { - return nil, err + val := validatorJSON{ + Address: v.Address, + VotingPower: v.VotingPower, + ProposerPriority: v.ProposerPriority, } - return json.Marshal(struct { - Addr Address `json:"address"` - PubKey json.RawMessage `json:"pub_key"` - Power int64 `json:"voting_power,string"` - Priority int64 `json:"proposer_priority,string"` - }{Addr: v.Address, PubKey: pk, Power: v.VotingPower, Priority: v.ProposerPriority}) + if v.PubKey != nil { + pk, err := jsontypes.Marshal(v.PubKey) + if err != nil { + return nil, err + } + val.PubKey = pk + } + return json.Marshal(val) +} + +func (v *Validator) UnmarshalJSON(data []byte) error { + var val validatorJSON + if err := json.Unmarshal(data, &val); err != nil { + return err + } + if err := jsontypes.Unmarshal(val.PubKey, &v.PubKey); err != nil { + return err + } + v.Address = val.Address + v.VotingPower = val.VotingPower + v.ProposerPriority = val.ProposerPriority + return nil } // NewValidator returns a new validator with the given pubkey and voting power.