Browse Source

rpc: use encoding/json rather than tmjson (#7670)

The main change here is to use encoding/json to encode and decode RPC 
parameters, rather than the custom tmjson package. This includes:

- Update the HTTP POST handler parameter handling.
- Add field tags to 64-bit integer types to get string encoding (to match amino/tmjson).
- Add marshalers to struct types that mention interfaces.
- Inject wrappers to decode interface arguments in RPC handlers.
pull/7679/head
M. J. Fromberger 3 years ago
committed by GitHub
parent
commit
f9c6cc9306
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 484 additions and 319 deletions
  1. +2
    -2
      cmd/tendermint/commands/gen_node_key.go
  2. +2
    -2
      cmd/tendermint/commands/gen_validator.go
  3. +2
    -2
      cmd/tendermint/commands/show_validator.go
  4. +2
    -2
      config/config.go
  5. +0
    -4
      crypto/ed25519/ed25519.go
  6. +0
    -4
      crypto/secp256k1/secp256k1.go
  7. +0
    -4
      crypto/sr25519/encoding.go
  8. +2
    -3
      internal/consensus/peer_state.go
  9. +10
    -1
      internal/jsontypes/jsontypes.go
  10. +2
    -2
      internal/rpc/core/env.go
  11. +6
    -9
      internal/rpc/core/evidence.go
  12. +1
    -1
      internal/rpc/core/routes.go
  13. +4
    -0
      light/proxy/routes.go
  14. +30
    -12
      privval/file.go
  15. +2
    -3
      privval/file_test.go
  16. +1
    -1
      rpc/client/http/http.go
  17. +2
    -17
      rpc/client/http/request.go
  18. +2
    -2
      rpc/client/http/ws.go
  19. +1
    -1
      rpc/client/local/local.go
  20. +1
    -1
      rpc/client/mock/client.go
  21. +3
    -3
      rpc/client/rpc_test.go
  22. +115
    -39
      rpc/coretypes/responses.go
  23. +2
    -3
      rpc/jsonrpc/client/decode.go
  24. +87
    -79
      rpc/jsonrpc/server/http_json_handler.go
  25. +1
    -1
      rpc/jsonrpc/server/http_json_handler_test.go
  26. +5
    -5
      rpc/jsonrpc/server/parse_test.go
  27. +7
    -12
      rpc/jsonrpc/server/ws_handler.go
  28. +6
    -33
      rpc/jsonrpc/types/types.go
  29. +2
    -2
      test/e2e/runner/evidence.go
  30. +50
    -16
      types/events.go
  31. +28
    -4
      types/evidence.go
  32. +31
    -14
      types/genesis.go
  33. +4
    -4
      types/genesis_test.go
  34. +3
    -3
      types/node_info.go
  35. +25
    -9
      types/node_key.go
  36. +6
    -6
      types/params.go
  37. +37
    -13
      types/validator.go

+ 2
- 2
cmd/tendermint/commands/gen_node_key.go View File

@ -1,11 +1,11 @@
package commands package commands
import ( import (
"encoding/json"
"fmt" "fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
tmjson "github.com/tendermint/tendermint/libs/json"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
) )
@ -20,7 +20,7 @@ var GenNodeKeyCmd = &cobra.Command{
func genNodeKey(cmd *cobra.Command, args []string) error { func genNodeKey(cmd *cobra.Command, args []string) error {
nodeKey := types.GenNodeKey() nodeKey := types.GenNodeKey()
bz, err := tmjson.Marshal(nodeKey)
bz, err := json.Marshal(nodeKey)
if err != nil { if err != nil {
return fmt.Errorf("nodeKey -> json: %w", err) return fmt.Errorf("nodeKey -> json: %w", err)
} }


+ 2
- 2
cmd/tendermint/commands/gen_validator.go View File

@ -1,11 +1,11 @@
package commands package commands
import ( import (
"encoding/json"
"fmt" "fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
tmjson "github.com/tendermint/tendermint/libs/json"
"github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/privval"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
) )
@ -29,7 +29,7 @@ func genValidator(cmd *cobra.Command, args []string) error {
return err return err
} }
jsbz, err := tmjson.Marshal(pv)
jsbz, err := json.Marshal(pv)
if err != nil { if err != nil {
return fmt.Errorf("validator -> json: %w", err) return fmt.Errorf("validator -> json: %w", err)
} }


+ 2
- 2
cmd/tendermint/commands/show_validator.go View File

@ -7,7 +7,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/tendermint/tendermint/crypto" "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" tmnet "github.com/tendermint/tendermint/libs/net"
tmos "github.com/tendermint/tendermint/libs/os" tmos "github.com/tendermint/tendermint/libs/os"
"github.com/tendermint/tendermint/privval" "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 { if err != nil {
return fmt.Errorf("failed to marshal private validator pubkey: %w", err) return fmt.Errorf("failed to marshal private validator pubkey: %w", err)
} }


+ 2
- 2
config/config.go View File

@ -2,6 +2,7 @@ package config
import ( import (
"encoding/hex" "encoding/hex"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
@ -9,7 +10,6 @@ import (
"path/filepath" "path/filepath"
"time" "time"
tmjson "github.com/tendermint/tendermint/libs/json"
"github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/libs/log"
tmos "github.com/tendermint/tendermint/libs/os" tmos "github.com/tendermint/tendermint/libs/os"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
@ -270,7 +270,7 @@ func (cfg BaseConfig) LoadNodeKeyID() (types.NodeID, error) {
return "", err return "", err
} }
nodeKey := types.NodeKey{} nodeKey := types.NodeKey{}
err = tmjson.Unmarshal(jsonBytes, &nodeKey)
err = json.Unmarshal(jsonBytes, &nodeKey)
if err != nil { if err != nil {
return "", err return "", err
} }


+ 0
- 4
crypto/ed25519/ed25519.go View File

@ -13,7 +13,6 @@ import (
"github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/tmhash" "github.com/tendermint/tendermint/crypto/tmhash"
"github.com/tendermint/tendermint/internal/jsontypes" "github.com/tendermint/tendermint/internal/jsontypes"
tmjson "github.com/tendermint/tendermint/libs/json"
) )
//------------------------------------- //-------------------------------------
@ -57,9 +56,6 @@ const (
) )
func init() { func init() {
tmjson.RegisterType(PubKey{}, PubKeyName)
tmjson.RegisterType(PrivKey{}, PrivKeyName)
jsontypes.MustRegister(PubKey{}) jsontypes.MustRegister(PubKey{})
jsontypes.MustRegister(PrivKey{}) jsontypes.MustRegister(PrivKey{})
} }


+ 0
- 4
crypto/secp256k1/secp256k1.go View File

@ -11,7 +11,6 @@ import (
secp256k1 "github.com/btcsuite/btcd/btcec" secp256k1 "github.com/btcsuite/btcd/btcec"
"github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/internal/jsontypes" "github.com/tendermint/tendermint/internal/jsontypes"
tmjson "github.com/tendermint/tendermint/libs/json"
// necessary for Bitcoin address format // necessary for Bitcoin address format
"golang.org/x/crypto/ripemd160" // nolint "golang.org/x/crypto/ripemd160" // nolint
@ -27,9 +26,6 @@ const (
) )
func init() { func init() {
tmjson.RegisterType(PubKey{}, PubKeyName)
tmjson.RegisterType(PrivKey{}, PrivKeyName)
jsontypes.MustRegister(PubKey{}) jsontypes.MustRegister(PubKey{})
jsontypes.MustRegister(PrivKey{}) jsontypes.MustRegister(PrivKey{})
} }


+ 0
- 4
crypto/sr25519/encoding.go View File

@ -2,7 +2,6 @@ package sr25519
import ( import (
"github.com/tendermint/tendermint/internal/jsontypes" "github.com/tendermint/tendermint/internal/jsontypes"
tmjson "github.com/tendermint/tendermint/libs/json"
) )
const ( const (
@ -11,9 +10,6 @@ const (
) )
func init() { func init() {
tmjson.RegisterType(PubKey{}, PubKeyName)
tmjson.RegisterType(PrivKey{}, PrivKeyName)
jsontypes.MustRegister(PubKey{}) jsontypes.MustRegister(PubKey{})
jsontypes.MustRegister(PrivKey{}) jsontypes.MustRegister(PrivKey{})
} }

+ 2
- 3
internal/consensus/peer_state.go View File

@ -1,6 +1,7 @@
package consensus package consensus
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"sync" "sync"
@ -9,7 +10,6 @@ import (
cstypes "github.com/tendermint/tendermint/internal/consensus/types" cstypes "github.com/tendermint/tendermint/internal/consensus/types"
tmsync "github.com/tendermint/tendermint/internal/libs/sync" tmsync "github.com/tendermint/tendermint/internal/libs/sync"
"github.com/tendermint/tendermint/libs/bits" "github.com/tendermint/tendermint/libs/bits"
tmjson "github.com/tendermint/tendermint/libs/json"
"github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/libs/log"
tmtime "github.com/tendermint/tendermint/libs/time" tmtime "github.com/tendermint/tendermint/libs/time"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
@ -96,8 +96,7 @@ func (ps *PeerState) GetRoundState() *cstypes.PeerRoundState {
func (ps *PeerState) ToJSON() ([]byte, error) { func (ps *PeerState) ToJSON() ([]byte, error) {
ps.mtx.Lock() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
return tmjson.Marshal(ps)
return json.Marshal(ps)
} }
// GetHeight returns an atomic snapshot of the PeerRoundState's height used by // GetHeight returns an atomic snapshot of the PeerRoundState's height used by


+ 10
- 1
internal/jsontypes/jsontypes.go View File

@ -84,6 +84,10 @@ func Unmarshal(data []byte, v interface{}) error {
return fmt.Errorf("target is a nil %T", v) return fmt.Errorf("target is a nil %T", v)
} }
baseType := target.Type().Elem() baseType := target.Type().Elem()
if isNull(data) {
target.Elem().Set(reflect.Zero(baseType))
return nil
}
var w wrapper var w wrapper
dec := json.NewDecoder(bytes.NewReader(data)) dec := json.NewDecoder(bytes.NewReader(data))
@ -93,7 +97,7 @@ func Unmarshal(data []byte, v interface{}) error {
} }
typ, ok := registry.types[w.Type] typ, ok := registry.types[w.Type]
if !ok { 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) { if typ.AssignableTo(baseType) {
// ok: registered type is directly assignable to the target // 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()) target.Elem().Set(obj.Elem())
return nil 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"))
}

+ 2
- 2
internal/rpc/core/env.go View File

@ -3,6 +3,7 @@ package core
import ( import (
"context" "context"
"encoding/base64" "encoding/base64"
"encoding/json"
"fmt" "fmt"
"net" "net"
"net/http" "net/http"
@ -21,7 +22,6 @@ import (
sm "github.com/tendermint/tendermint/internal/state" sm "github.com/tendermint/tendermint/internal/state"
"github.com/tendermint/tendermint/internal/state/indexer" "github.com/tendermint/tendermint/internal/state/indexer"
"github.com/tendermint/tendermint/internal/statesync" "github.com/tendermint/tendermint/internal/statesync"
tmjson "github.com/tendermint/tendermint/libs/json"
"github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/libs/strings" "github.com/tendermint/tendermint/libs/strings"
"github.com/tendermint/tendermint/rpc/coretypes" "github.com/tendermint/tendermint/rpc/coretypes"
@ -154,7 +154,7 @@ func (env *Environment) InitGenesisChunks() error {
return nil return nil
} }
data, err := tmjson.Marshal(env.GenDoc)
data, err := json.Marshal(env.GenDoc)
if err != nil { if err != nil {
return err return err
} }


+ 6
- 9
internal/rpc/core/evidence.go View File

@ -5,25 +5,22 @@ import (
"fmt" "fmt"
"github.com/tendermint/tendermint/rpc/coretypes" "github.com/tendermint/tendermint/rpc/coretypes"
"github.com/tendermint/tendermint/types"
) )
// BroadcastEvidence broadcasts evidence of the misbehavior. // BroadcastEvidence broadcasts evidence of the misbehavior.
// More: https://docs.tendermint.com/master/rpc/#/Evidence/broadcast_evidence // More: https://docs.tendermint.com/master/rpc/#/Evidence/broadcast_evidence
func (env *Environment) BroadcastEvidence( func (env *Environment) BroadcastEvidence(
ctx context.Context, 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) 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) 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 nil, fmt.Errorf("failed to add evidence: %w", err)
} }
return &coretypes.ResultBroadcastEvidence{Hash: ev.Hash()}, nil
return &coretypes.ResultBroadcastEvidence{Hash: ev.Value.Hash()}, nil
} }

+ 1
- 1
internal/rpc/core/routes.go View File

@ -86,7 +86,7 @@ type RPCService interface {
BlockResults(ctx context.Context, heightPtr *int64) (*coretypes.ResultBlockResults, error) BlockResults(ctx context.Context, heightPtr *int64) (*coretypes.ResultBlockResults, error)
BlockSearch(ctx context.Context, query string, pagePtr, perPagePtr *int, orderBy string) (*coretypes.ResultBlockSearch, 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) 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) BroadcastTxAsync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error)
BroadcastTxCommit(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTxCommit, error) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTxCommit, error)
BroadcastTxSync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error) BroadcastTxSync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error)


+ 4
- 0
light/proxy/routes.go View File

@ -38,3 +38,7 @@ func (p proxyService) Unsubscribe(ctx context.Context, query string) (*coretypes
func (p proxyService) UnsubscribeAll(ctx context.Context) (*coretypes.ResultUnsubscribe, error) { func (p proxyService) UnsubscribeAll(ctx context.Context) (*coretypes.ResultUnsubscribe, error) {
return p.UnsubscribeAllWS(ctx) return p.UnsubscribeAllWS(ctx)
} }
func (p proxyService) BroadcastEvidence(ctx context.Context, ev coretypes.Evidence) (*coretypes.ResultBroadcastEvidence, error) {
return p.Client.BroadcastEvidence(ctx, ev.Value)
}

+ 30
- 12
privval/file.go View File

@ -18,7 +18,6 @@ import (
"github.com/tendermint/tendermint/internal/libs/protoio" "github.com/tendermint/tendermint/internal/libs/protoio"
"github.com/tendermint/tendermint/internal/libs/tempfile" "github.com/tendermint/tendermint/internal/libs/tempfile"
tmbytes "github.com/tendermint/tendermint/libs/bytes" tmbytes "github.com/tendermint/tendermint/libs/bytes"
tmjson "github.com/tendermint/tendermint/libs/json"
tmos "github.com/tendermint/tendermint/libs/os" tmos "github.com/tendermint/tendermint/libs/os"
tmtime "github.com/tendermint/tendermint/libs/time" tmtime "github.com/tendermint/tendermint/libs/time"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 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. // FilePVKey stores the immutable part of PrivValidator.
type FilePVKey struct { 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 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) { func (pvKey FilePVKey) MarshalJSON() ([]byte, error) {
pubk, err := jsontypes.Marshal(pvKey.PubKey) pubk, err := jsontypes.Marshal(pvKey.PubKey)
if err != nil { if err != nil {
@ -65,11 +70,24 @@ func (pvKey FilePVKey) MarshalJSON() ([]byte, error) {
if err != nil { if err != nil {
return nil, err 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. // 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") return errors.New("cannot save PrivValidator key: filePath not set")
} }
jsonBytes, err := tmjson.MarshalIndent(pvKey, "", " ")
data, err := json.MarshalIndent(pvKey, "", " ")
if err != nil { if err != nil {
return err 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 return nil, err
} }
pvKey := FilePVKey{} pvKey := FilePVKey{}
err = tmjson.Unmarshal(keyJSONBytes, &pvKey)
err = json.Unmarshal(keyJSONBytes, &pvKey)
if err != nil { if err != nil {
return nil, fmt.Errorf("error reading PrivValidator key from %v: %w", keyFilePath, err) return nil, fmt.Errorf("error reading PrivValidator key from %v: %w", keyFilePath, err)
} }


+ 2
- 3
privval/file_test.go View File

@ -14,7 +14,6 @@ import (
"github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/tmhash" "github.com/tendermint/tendermint/crypto/tmhash"
tmjson "github.com/tendermint/tendermint/libs/json"
tmrand "github.com/tendermint/tendermint/libs/rand" tmrand "github.com/tendermint/tendermint/libs/rand"
tmtime "github.com/tendermint/tendermint/libs/time" tmtime "github.com/tendermint/tendermint/libs/time"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
@ -143,7 +142,7 @@ func TestUnmarshalValidatorKey(t *testing.T) {
}`, addr, pubB64, privB64) }`, addr, pubB64, privB64)
val := FilePVKey{} val := FilePVKey{}
err := tmjson.Unmarshal([]byte(serialized), &val)
err := json.Unmarshal([]byte(serialized), &val)
require.NoError(t, err) require.NoError(t, err)
// make sure the values match // make sure the values match
@ -152,7 +151,7 @@ func TestUnmarshalValidatorKey(t *testing.T) {
assert.EqualValues(t, privKey, val.PrivKey) assert.EqualValues(t, privKey, val.PrivKey)
// export it and make sure it is the same // export it and make sure it is the same
out, err := tmjson.Marshal(val)
out, err := json.Marshal(val)
require.NoError(t, err) require.NoError(t, err)
assert.JSONEq(t, serialized, string(out)) assert.JSONEq(t, serialized, string(out))
} }


+ 1
- 1
rpc/client/http/http.go View File

@ -512,7 +512,7 @@ func (c *baseRPCClient) BroadcastEvidence(
) (*coretypes.ResultBroadcastEvidence, error) { ) (*coretypes.ResultBroadcastEvidence, error) {
result := new(coretypes.ResultBroadcastEvidence) result := new(coretypes.ResultBroadcastEvidence)
if err := c.caller.Call(ctx, "broadcast_evidence", evidenceArgs{ if err := c.caller.Call(ctx, "broadcast_evidence", evidenceArgs{
Evidence: ev,
Evidence: coretypes.Evidence{Value: ev},
}, result); err != nil { }, result); err != nil {
return nil, err return nil, err
} }


+ 2
- 17
rpc/client/http/request.go View File

@ -4,11 +4,8 @@ package http
// from the client to the server. // from the client to the server.
import ( import (
"encoding/json"
"github.com/tendermint/tendermint/internal/jsontypes"
"github.com/tendermint/tendermint/libs/bytes" "github.com/tendermint/tendermint/libs/bytes"
"github.com/tendermint/tendermint/types"
"github.com/tendermint/tendermint/rpc/coretypes"
) )
type abciQueryArgs struct { type abciQueryArgs struct {
@ -64,17 +61,5 @@ type validatorArgs struct {
} }
type evidenceArgs 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"`
} }

+ 2
- 2
rpc/client/http/ws.go View File

@ -2,6 +2,7 @@ package http
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"strings" "strings"
@ -9,7 +10,6 @@ import (
"time" "time"
"github.com/tendermint/tendermint/internal/pubsub" "github.com/tendermint/tendermint/internal/pubsub"
tmjson "github.com/tendermint/tendermint/libs/json"
rpcclient "github.com/tendermint/tendermint/rpc/client" rpcclient "github.com/tendermint/tendermint/rpc/client"
"github.com/tendermint/tendermint/rpc/coretypes" "github.com/tendermint/tendermint/rpc/coretypes"
jsonrpcclient "github.com/tendermint/tendermint/rpc/jsonrpc/client" jsonrpcclient "github.com/tendermint/tendermint/rpc/jsonrpc/client"
@ -239,7 +239,7 @@ func (w *wsEvents) eventListener(ctx context.Context) {
} }
result := new(coretypes.ResultEvent) result := new(coretypes.ResultEvent)
err := tmjson.Unmarshal(resp.Result, result)
err := json.Unmarshal(resp.Result, result)
if err != nil { if err != nil {
w.Logger.Error("failed to unmarshal response", "err", err) w.Logger.Error("failed to unmarshal response", "err", err)
continue continue


+ 1
- 1
rpc/client/local/local.go View File

@ -198,7 +198,7 @@ func (c *Local) BlockSearch(
} }
func (c *Local) BroadcastEvidence(ctx context.Context, ev types.Evidence) (*coretypes.ResultBroadcastEvidence, error) { 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( func (c *Local) Subscribe(


+ 1
- 1
rpc/client/mock/client.go View File

@ -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) { 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})
} }

+ 3
- 3
rpc/client/rpc_test.go View File

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/base64" "encoding/base64"
"encoding/json"
"fmt" "fmt"
"math" "math"
"net/http" "net/http"
@ -21,7 +22,6 @@ import (
"github.com/tendermint/tendermint/crypto/encoding" "github.com/tendermint/tendermint/crypto/encoding"
"github.com/tendermint/tendermint/internal/mempool" "github.com/tendermint/tendermint/internal/mempool"
rpccore "github.com/tendermint/tendermint/internal/rpc/core" rpccore "github.com/tendermint/tendermint/internal/rpc/core"
tmjson "github.com/tendermint/tendermint/libs/json"
"github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/libs/log"
tmmath "github.com/tendermint/tendermint/libs/math" tmmath "github.com/tendermint/tendermint/libs/math"
"github.com/tendermint/tendermint/libs/service" "github.com/tendermint/tendermint/libs/service"
@ -305,7 +305,7 @@ func TestClientMethodCalls(t *testing.T) {
doc := []byte(strings.Join(decoded, "")) doc := []byte(strings.Join(decoded, ""))
var out types.GenesisDoc 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)) "first: %+v, doc: %s", first, string(doc))
}) })
t.Run("ABCIQuery", func(t *testing.T) { t.Run("ABCIQuery", func(t *testing.T) {
@ -582,7 +582,7 @@ func TestClientMethodCalls(t *testing.T) {
}) })
t.Run("BroadcastEmpty", func(t *testing.T) { t.Run("BroadcastEmpty", func(t *testing.T) {
_, err := c.BroadcastEvidence(ctx, nil) _, err := c.BroadcastEvidence(ctx, nil)
assert.Error(t, err)
require.Error(t, err)
}) })
}) })
}) })


+ 115
- 39
rpc/coretypes/responses.go View File

@ -3,10 +3,12 @@ package coretypes
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"time" "time"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/internal/jsontypes"
"github.com/tendermint/tendermint/libs/bytes" "github.com/tendermint/tendermint/libs/bytes"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
@ -26,7 +28,7 @@ var (
// List of blocks // List of blocks
type ResultBlockchainInfo struct { type ResultBlockchainInfo struct {
LastHeight int64 `json:"last_height"`
LastHeight int64 `json:"last_height,string"`
BlockMetas []*types.BlockMeta `json:"block_metas"` BlockMetas []*types.BlockMeta `json:"block_metas"`
} }
@ -40,8 +42,8 @@ type ResultGenesis struct {
// document to JSON and then splitting the resulting payload into // document to JSON and then splitting the resulting payload into
// 16 megabyte blocks and then base64 encoding each block. // 16 megabyte blocks and then base64 encoding each block.
type ResultGenesisChunk struct { 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"` Data string `json:"data"`
} }
@ -64,9 +66,9 @@ type ResultCommit struct {
// ABCI results from a block // ABCI results from a block
type ResultBlockResults struct { type ResultBlockResults struct {
Height int64 `json:"height"`
Height int64 `json:"height,string"`
TxsResults []*abci.ResponseDeliverTx `json:"txs_results"` 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"` BeginBlockEvents []abci.Event `json:"begin_block_events"`
EndBlockEvents []abci.Event `json:"end_block_events"` EndBlockEvents []abci.Event `json:"end_block_events"`
ValidatorUpdates []abci.ValidatorUpdate `json:"validator_updates"` ValidatorUpdates []abci.ValidatorUpdate `json:"validator_updates"`
@ -91,35 +93,64 @@ func NewResultCommit(header *types.Header, commit *types.Commit,
type SyncInfo struct { type SyncInfo struct {
LatestBlockHash bytes.HexBytes `json:"latest_block_hash"` LatestBlockHash bytes.HexBytes `json:"latest_block_hash"`
LatestAppHash bytes.HexBytes `json:"latest_app_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"` LatestBlockTime time.Time `json:"latest_block_time"`
EarliestBlockHash bytes.HexBytes `json:"earliest_block_hash"` EarliestBlockHash bytes.HexBytes `json:"earliest_block_hash"`
EarliestAppHash bytes.HexBytes `json:"earliest_app_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"` 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"` 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 // Info about the node's validator
type ValidatorInfo struct { 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 // Node Status
@ -142,7 +173,7 @@ func (s *ResultStatus) TxIndexEnabled() bool {
type ResultNetInfo struct { type ResultNetInfo struct {
Listening bool `json:"listening"` Listening bool `json:"listening"`
Listeners []string `json:"listeners"` Listeners []string `json:"listeners"`
NPeers int `json:"n_peers"`
NPeers int `json:"n_peers,string"`
Peers []Peer `json:"peers"` Peers []Peer `json:"peers"`
} }
@ -164,12 +195,11 @@ type Peer struct {
// Validators for a height. // Validators for a height.
type ResultValidators struct { type ResultValidators struct {
BlockHeight int64 `json:"block_height"`
BlockHeight int64 `json:"block_height,string"`
Validators []*types.Validator `json:"validators"` 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 // ConsensusParams for given height
@ -203,8 +233,7 @@ type ResultBroadcastTx struct {
Log string `json:"log"` Log string `json:"log"`
Codespace string `json:"codespace"` Codespace string `json:"codespace"`
MempoolError string `json:"mempool_error"` MempoolError string `json:"mempool_error"`
Hash bytes.HexBytes `json:"hash"`
Hash bytes.HexBytes `json:"hash"`
} }
// CheckTx and DeliverTx results // CheckTx and DeliverTx results
@ -212,7 +241,7 @@ type ResultBroadcastTxCommit struct {
CheckTx abci.ResponseCheckTx `json:"check_tx"` CheckTx abci.ResponseCheckTx `json:"check_tx"`
DeliverTx abci.ResponseDeliverTx `json:"deliver_tx"` DeliverTx abci.ResponseDeliverTx `json:"deliver_tx"`
Hash bytes.HexBytes `json:"hash"` Hash bytes.HexBytes `json:"hash"`
Height int64 `json:"height"`
Height int64 `json:"height,string"`
} }
// ResultCheckTx wraps abci.ResponseCheckTx. // ResultCheckTx wraps abci.ResponseCheckTx.
@ -223,7 +252,7 @@ type ResultCheckTx struct {
// Result of querying for a tx // Result of querying for a tx
type ResultTx struct { type ResultTx struct {
Hash bytes.HexBytes `json:"hash"` Hash bytes.HexBytes `json:"hash"`
Height int64 `json:"height"`
Height int64 `json:"height,string"`
Index uint32 `json:"index"` Index uint32 `json:"index"`
TxResult abci.ResponseDeliverTx `json:"tx_result"` TxResult abci.ResponseDeliverTx `json:"tx_result"`
Tx types.Tx `json:"tx"` Tx types.Tx `json:"tx"`
@ -233,20 +262,20 @@ type ResultTx struct {
// Result of searching for txs // Result of searching for txs
type ResultTxSearch struct { type ResultTxSearch struct {
Txs []*ResultTx `json:"txs"` 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. // ResultBlockSearch defines the RPC response type for a block search by events.
type ResultBlockSearch struct { type ResultBlockSearch struct {
Blocks []*ResultBlock `json:"blocks"` Blocks []*ResultBlock `json:"blocks"`
TotalCount int `json:"total_count"`
TotalCount int `json:"total_count,string"`
} }
// List of mempool txs // List of mempool txs
type ResultUnconfirmedTxs struct { 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"` Txs []types.Tx `json:"txs"`
} }
@ -276,8 +305,55 @@ type (
// Event data from a subscription // Event data from a subscription
type ResultEvent struct { 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) }

+ 2
- 3
rpc/jsonrpc/client/decode.go View File

@ -5,7 +5,6 @@ import (
"errors" "errors"
"fmt" "fmt"
tmjson "github.com/tendermint/tendermint/libs/json"
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types" 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. // 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 fmt.Errorf("error unmarshaling result: %w", err)
} }
return nil return nil
@ -71,7 +70,7 @@ func unmarshalResponseBytesArray(
} }
for i := 0; i < len(responses); i++ { 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) return nil, fmt.Errorf("error unmarshaling #%d result: %w", i, err)
} }
} }


+ 87
- 79
rpc/jsonrpc/server/http_json_handler.go View File

@ -2,6 +2,7 @@ package server
import ( import (
"bytes" "bytes"
"context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@ -9,8 +10,8 @@ import (
"net/http" "net/http"
"reflect" "reflect"
"sort" "sort"
"strconv"
tmjson "github.com/tendermint/tendermint/libs/json"
"github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/rpc/coretypes" "github.com/tendermint/tendermint/rpc/coretypes"
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types" rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
@ -63,7 +64,12 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han
continue 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 { if err != nil {
responses = append(responses, rpctypes.RPCInvalidParamsError( responses = append(responses, rpctypes.RPCInvalidParamsError(
req.ID, fmt.Errorf("converting JSON parameters: %w", err))) req.ID, fmt.Errorf("converting JSON parameters: %w", err)))
@ -132,99 +138,101 @@ func parseRequests(data []byte) ([]rpctypes.RPCRequest, error) {
return reqs, nil 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 // writes a list of available rpc endpoints as an html page


+ 1
- 1
rpc/jsonrpc/server/http_json_handler_test.go View File

@ -46,7 +46,7 @@ func TestRPCParams(t *testing.T) {
// id not captured in JSON parsing failures // id not captured in JSON parsing failures
{`{"method": "c", "id": "0", "params": a}`, "invalid character", nil}, {`{"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"]}`, "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")}, {`{"method": "c", "id": "0", "params": [1, 1]}`, "of type string", rpctypes.JSONRPCStringID("0")},
// no ID - notification // no ID - notification


+ 5
- 5
rpc/jsonrpc/server/parse_test.go View File

@ -153,17 +153,17 @@ func TestParseJSONRPC(t *testing.T) {
{`[7,"flew",100]`, 0, "", true}, {`[7,"flew",100]`, 0, "", true},
{`{"name": -12, "height": "fred"}`, 0, "", true}, {`{"name": -12, "height": "fred"}`, 0, "", true},
} }
ctx := context.Background()
for idx, tc := range cases { for idx, tc := range cases {
i := strconv.Itoa(idx) i := strconv.Itoa(idx)
data := []byte(tc.raw)
vals, err := jsonParamsToArgs(call, data)
vals, err := parseParams(ctx, call, []byte(tc.raw))
if tc.fail { if tc.fail {
assert.Error(t, err, i) assert.Error(t, err, i)
} else { } else {
assert.NoError(t, err, "%s: %+v", i, err) 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)
} }
} }


+ 7
- 12
rpc/jsonrpc/server/ws_handler.go View File

@ -6,7 +6,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"reflect"
"runtime/debug" "runtime/debug"
"time" "time"
@ -371,18 +370,14 @@ func (wsc *wsConnection) readRoutine(ctx context.Context) {
RPCRequest: &request, RPCRequest: &request,
WSConn: wsc, 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) returns := rpcFunc.f.Call(args)


+ 6
- 33
rpc/jsonrpc/types/types.go View File

@ -7,8 +7,6 @@ import (
"net/http" "net/http"
"reflect" "reflect"
"strings" "strings"
tmjson "github.com/tendermint/tendermint/libs/json"
) )
// a wrapper to emulate a sum type: jsonrpcid = string | int // 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. // ParamsToRequest constructs a new RPCRequest with the given ID, method, and parameters.
func ParamsToRequest(id jsonrpcid, method string, params interface{}) (RPCRequest, error) { 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 { if err != nil {
return RPCRequest{}, err return RPCRequest{}, err
} }
return NewRPCRequest(id, method, payload), nil return NewRPCRequest(id, method, payload), nil
} }
@ -162,6 +141,7 @@ func (resp *RPCResponse) UnmarshalJSON(data []byte) error {
if err != nil { if err != nil {
return err return err
} }
resp.JSONRPC = unsafeResp.JSONRPC resp.JSONRPC = unsafeResp.JSONRPC
resp.Error = unsafeResp.Error resp.Error = unsafeResp.Error
resp.Result = unsafeResp.Result resp.Result = unsafeResp.Result
@ -177,18 +157,11 @@ func (resp *RPCResponse) UnmarshalJSON(data []byte) error {
} }
func NewRPCSuccessResponse(id jsonrpcid, res interface{}) RPCResponse { 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 { func NewRPCErrorResponse(id jsonrpcid, code int, msg string, data string) RPCResponse {


+ 2
- 2
test/e2e/runner/evidence.go View File

@ -3,6 +3,7 @@ package main
import ( import (
"bytes" "bytes"
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"math/rand" "math/rand"
@ -13,7 +14,6 @@ import (
"github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/tmhash" "github.com/tendermint/tendermint/crypto/tmhash"
"github.com/tendermint/tendermint/internal/test/factory" "github.com/tendermint/tendermint/internal/test/factory"
tmjson "github.com/tendermint/tendermint/libs/json"
"github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/privval"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
e2e "github.com/tendermint/tendermint/test/e2e/pkg" e2e "github.com/tendermint/tendermint/test/e2e/pkg"
@ -241,7 +241,7 @@ func readPrivKey(keyFilePath string) (crypto.PrivKey, error) {
return nil, err return nil, err
} }
pvKey := privval.FilePVKey{} pvKey := privval.FilePVKey{}
err = tmjson.Unmarshal(keyJSONBytes, &pvKey)
err = json.Unmarshal(keyJSONBytes, &pvKey)
if err != nil { if err != nil {
return nil, fmt.Errorf("error reading PrivValidator key from %v: %w", keyFilePath, err) return nil, fmt.Errorf("error reading PrivValidator key from %v: %w", keyFilePath, err)
} }


+ 50
- 16
types/events.go View File

@ -6,9 +6,9 @@ import (
"strings" "strings"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/internal/jsontypes"
tmpubsub "github.com/tendermint/tendermint/internal/pubsub" tmpubsub "github.com/tendermint/tendermint/internal/pubsub"
tmquery "github.com/tendermint/tendermint/internal/pubsub/query" tmquery "github.com/tendermint/tendermint/internal/pubsub/query"
tmjson "github.com/tendermint/tendermint/libs/json"
) )
// Reserved event types (alphabetically sorted). // Reserved event types (alphabetically sorted).
@ -90,23 +90,21 @@ var (
// ENCODING / DECODING // ENCODING / DECODING
// TMEventData implements events.EventData. // TMEventData implements events.EventData.
type TMEventData interface {
// empty interface
}
type TMEventData interface{}
func init() { 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) // Most event messages are basic types (a block, a transaction)
@ -120,6 +118,9 @@ type EventDataNewBlock struct {
ResultEndBlock abci.ResponseEndBlock `json:"result_end_block"` 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 { type EventDataNewBlockHeader struct {
Header Header `json:"header"` Header Header `json:"header"`
@ -128,17 +129,26 @@ type EventDataNewBlockHeader struct {
ResultEndBlock abci.ResponseEndBlock `json:"result_end_block"` 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 { type EventDataNewEvidence struct {
Evidence Evidence `json:"evidence"` Evidence Evidence `json:"evidence"`
Height int64 `json:"height"` Height int64 `json:"height"`
} }
// TypeTag implements the required method of jsontypes.Tagged.
func (EventDataNewEvidence) TypeTag() string { return "tendermint/event/NewEvidence" }
// All txs fire EventDataTx // All txs fire EventDataTx
type EventDataTx struct { type EventDataTx struct {
abci.TxResult 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 // NOTE: This goes into the replay WAL
type EventDataRoundState struct { type EventDataRoundState struct {
Height int64 `json:"height"` Height int64 `json:"height"`
@ -146,6 +156,9 @@ type EventDataRoundState struct {
Step string `json:"step"` Step string `json:"step"`
} }
// TypeTag implements the required method of jsontypes.Tagged.
func (EventDataRoundState) TypeTag() string { return "tendermint/event/RoundState" }
type ValidatorInfo struct { type ValidatorInfo struct {
Address Address `json:"address"` Address Address `json:"address"`
Index int32 `json:"index"` Index int32 `json:"index"`
@ -159,6 +172,9 @@ type EventDataNewRound struct {
Proposer ValidatorInfo `json:"proposer"` Proposer ValidatorInfo `json:"proposer"`
} }
// TypeTag implements the required method of jsontypes.Tagged.
func (EventDataNewRound) TypeTag() string { return "tendermint/event/NewRound" }
type EventDataCompleteProposal struct { type EventDataCompleteProposal struct {
Height int64 `json:"height"` Height int64 `json:"height"`
Round int32 `json:"round"` Round int32 `json:"round"`
@ -167,16 +183,28 @@ type EventDataCompleteProposal struct {
BlockID BlockID `json:"block_id"` BlockID BlockID `json:"block_id"`
} }
// TypeTag implements the required method of jsontypes.Tagged.
func (EventDataCompleteProposal) TypeTag() string { return "tendermint/event/CompleteProposal" }
type EventDataVote struct { type EventDataVote struct {
Vote *Vote Vote *Vote
} }
// TypeTag implements the required method of jsontypes.Tagged.
func (EventDataVote) TypeTag() string { return "tendermint/event/Vote" }
type EventDataString string type EventDataString string
// TypeTag implements the required method of jsontypes.Tagged.
func (EventDataString) TypeTag() string { return "tendermint/event/ProposalString" }
type EventDataValidatorSetUpdates struct { type EventDataValidatorSetUpdates struct {
ValidatorUpdates []*Validator `json:"validator_updates"` 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 // EventDataBlockSyncStatus shows the fastsync status and the
// height when the node state sync mechanism changes. // height when the node state sync mechanism changes.
type EventDataBlockSyncStatus struct { type EventDataBlockSyncStatus struct {
@ -184,6 +212,9 @@ type EventDataBlockSyncStatus struct {
Height int64 `json:"height"` 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 // EventDataStateSyncStatus shows the statesync status and the
// height when the node state sync mechanism changes. // height when the node state sync mechanism changes.
type EventDataStateSyncStatus struct { type EventDataStateSyncStatus struct {
@ -191,6 +222,9 @@ type EventDataStateSyncStatus struct {
Height int64 `json:"height"` Height int64 `json:"height"`
} }
// TypeTag implements the required method of jsontypes.Tagged.
func (EventDataStateSyncStatus) TypeTag() string { return "tendermint/event/StateSyncStatus" }
// PUBSUB // PUBSUB
const ( const (


+ 28
- 4
types/evidence.go View File

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/binary" "encoding/binary"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"sort" "sort"
@ -14,7 +15,6 @@ import (
"github.com/tendermint/tendermint/crypto/merkle" "github.com/tendermint/tendermint/crypto/merkle"
"github.com/tendermint/tendermint/crypto/tmhash" "github.com/tendermint/tendermint/crypto/tmhash"
"github.com/tendermint/tendermint/internal/jsontypes" "github.com/tendermint/tendermint/internal/jsontypes"
tmjson "github.com/tendermint/tendermint/libs/json"
tmrand "github.com/tendermint/tendermint/libs/rand" tmrand "github.com/tendermint/tendermint/libs/rand"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 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. // EvidenceList is a list of Evidence. Evidences is not a word.
type EvidenceList []Evidence 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. // Hash returns the simple merkle root hash of the EvidenceList.
func (evl EvidenceList) Hash() []byte { func (evl EvidenceList) Hash() []byte {
// These allocations are required because Evidence is not of type Bytes, and // 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() { func init() {
tmjson.RegisterType(&DuplicateVoteEvidence{}, "tendermint/DuplicateVoteEvidence")
tmjson.RegisterType(&LightClientAttackEvidence{}, "tendermint/LightClientAttackEvidence")
jsontypes.MustRegister((*DuplicateVoteEvidence)(nil)) jsontypes.MustRegister((*DuplicateVoteEvidence)(nil))
jsontypes.MustRegister((*LightClientAttackEvidence)(nil)) jsontypes.MustRegister((*LightClientAttackEvidence)(nil))
} }


+ 31
- 14
types/genesis.go View File

@ -11,7 +11,6 @@ import (
"github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/internal/jsontypes" "github.com/tendermint/tendermint/internal/jsontypes"
tmbytes "github.com/tendermint/tendermint/libs/bytes" tmbytes "github.com/tendermint/tendermint/libs/bytes"
tmjson "github.com/tendermint/tendermint/libs/json"
tmtime "github.com/tendermint/tendermint/libs/time" tmtime "github.com/tendermint/tendermint/libs/time"
) )
@ -28,10 +27,17 @@ const (
// GenesisValidator is an initial validator. // GenesisValidator is an initial validator.
type GenesisValidator struct { 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) { func (g GenesisValidator) MarshalJSON() ([]byte, error) {
@ -39,19 +45,30 @@ func (g GenesisValidator) MarshalJSON() ([]byte, error) {
if err != nil { if err != nil {
return nil, err 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. // GenesisDoc defines the initial conditions for a tendermint blockchain, in particular its validator set.
type GenesisDoc struct { type GenesisDoc struct {
GenesisTime time.Time `json:"genesis_time"` GenesisTime time.Time `json:"genesis_time"`
ChainID string `json:"chain_id"` ChainID string `json:"chain_id"`
InitialHeight int64 `json:"initial_height"`
InitialHeight int64 `json:"initial_height,string"`
ConsensusParams *ConsensusParams `json:"consensus_params,omitempty"` ConsensusParams *ConsensusParams `json:"consensus_params,omitempty"`
Validators []GenesisValidator `json:"validators,omitempty"` Validators []GenesisValidator `json:"validators,omitempty"`
AppHash tmbytes.HexBytes `json:"app_hash"` 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. // SaveAs is a utility method for saving GenensisDoc as a JSON file.
func (genDoc *GenesisDoc) SaveAs(file string) error { func (genDoc *GenesisDoc) SaveAs(file string) error {
genDocBytes, err := tmjson.MarshalIndent(genDoc, "", " ")
genDocBytes, err := json.MarshalIndent(genDoc, "", " ")
if err != nil { if err != nil {
return err return err
} }
@ -125,7 +142,7 @@ func (genDoc *GenesisDoc) ValidateAndComplete() error {
// GenesisDocFromJSON unmarshalls JSON data into a GenesisDoc. // GenesisDocFromJSON unmarshalls JSON data into a GenesisDoc.
func GenesisDocFromJSON(jsonBlob []byte) (*GenesisDoc, error) { func GenesisDocFromJSON(jsonBlob []byte) (*GenesisDoc, error) {
genDoc := GenesisDoc{} genDoc := GenesisDoc{}
err := tmjson.Unmarshal(jsonBlob, &genDoc)
err := json.Unmarshal(jsonBlob, &genDoc)
if err != nil { if err != nil {
return nil, err return nil, err
} }


+ 4
- 4
types/genesis_test.go View File

@ -1,6 +1,7 @@
package types package types
import ( import (
"encoding/json"
"os" "os"
"testing" "testing"
@ -8,7 +9,6 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/ed25519"
tmjson "github.com/tendermint/tendermint/libs/json"
tmtime "github.com/tendermint/tendermint/libs/time" tmtime "github.com/tendermint/tendermint/libs/time"
) )
@ -82,7 +82,7 @@ func TestGenesisGood(t *testing.T) {
ChainID: "abc", ChainID: "abc",
Validators: []GenesisValidator{{pubkey.Address(), pubkey, 10, "myval"}}, Validators: []GenesisValidator{{pubkey.Address(), pubkey, 10, "myval"}},
} }
genDocBytes, err = tmjson.Marshal(baseGenDoc)
genDocBytes, err = json.Marshal(baseGenDoc)
assert.NoError(t, err, "error marshaling genDoc") assert.NoError(t, err, "error marshaling genDoc")
// test base gendoc and check consensus params were filled // 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") assert.NotNil(t, genDoc.Validators[0].Address, "expected validator's address to be filled in")
// create json with consensus params filled // create json with consensus params filled
genDocBytes, err = tmjson.Marshal(genDoc)
genDocBytes, err = json.Marshal(genDoc)
assert.NoError(t, err, "error marshaling genDoc") assert.NoError(t, err, "error marshaling genDoc")
genDoc, err = GenesisDocFromJSON(genDocBytes) genDoc, err = GenesisDocFromJSON(genDocBytes)
assert.NoError(t, err, "expected no error for valid genDoc json") assert.NoError(t, err, "expected no error for valid genDoc json")
// test with invalid consensus params // test with invalid consensus params
genDoc.ConsensusParams.Block.MaxBytes = 0 genDoc.ConsensusParams.Block.MaxBytes = 0
genDocBytes, err = tmjson.Marshal(genDoc)
genDocBytes, err = json.Marshal(genDoc)
assert.NoError(t, err, "error marshaling genDoc") assert.NoError(t, err, "error marshaling genDoc")
_, err = GenesisDocFromJSON(genDocBytes) _, err = GenesisDocFromJSON(genDocBytes)
assert.Error(t, err, "expected error for genDoc json with block size of 0") assert.Error(t, err, "expected error for genDoc json with block size of 0")


+ 3
- 3
types/node_info.go View File

@ -24,9 +24,9 @@ func MaxNodeInfoSize() int {
// ProtocolVersion contains the protocol versions for the software. // ProtocolVersion contains the protocol versions for the software.
type ProtocolVersion struct { 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"`
} }
//------------------------------------------------------------- //-------------------------------------------------------------


+ 25
- 9
types/node_key.go View File

@ -7,7 +7,6 @@ import (
"github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/internal/jsontypes" "github.com/tendermint/tendermint/internal/jsontypes"
tmjson "github.com/tendermint/tendermint/libs/json"
tmos "github.com/tendermint/tendermint/libs/os" tmos "github.com/tendermint/tendermint/libs/os"
) )
@ -19,9 +18,14 @@ import (
// It contains the nodes private key for authentication. // It contains the nodes private key for authentication.
type NodeKey struct { type NodeKey struct {
// Canonical ID - hex-encoded pubkey's address (IDByteLength bytes) // Canonical ID - hex-encoded pubkey's address (IDByteLength bytes)
ID NodeID `json:"id"`
ID NodeID
// Private key // 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) { func (nk NodeKey) MarshalJSON() ([]byte, error) {
@ -29,10 +33,22 @@ func (nk NodeKey) MarshalJSON() ([]byte, error) {
if err != nil { if err != nil {
return nil, err 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 // PubKey returns the peer's PubKey
@ -42,7 +58,7 @@ func (nk NodeKey) PubKey() crypto.PubKey {
// SaveAs persists the NodeKey to filePath. // SaveAs persists the NodeKey to filePath.
func (nk NodeKey) SaveAs(filePath string) error { func (nk NodeKey) SaveAs(filePath string) error {
jsonBytes, err := tmjson.Marshal(nk)
jsonBytes, err := json.Marshal(nk)
if err != nil { if err != nil {
return err return err
} }
@ -85,7 +101,7 @@ func LoadNodeKey(filePath string) (NodeKey, error) {
return NodeKey{}, err return NodeKey{}, err
} }
nodeKey := NodeKey{} nodeKey := NodeKey{}
err = tmjson.Unmarshal(jsonBytes, &nodeKey)
err = json.Unmarshal(jsonBytes, &nodeKey)
if err != nil { if err != nil {
return NodeKey{}, err return NodeKey{}, err
} }


+ 6
- 6
types/params.go View File

@ -54,15 +54,15 @@ type HashedParams struct {
// BlockParams define limits on the block size and gas plus minimum time // BlockParams define limits on the block size and gas plus minimum time
// between blocks. // between blocks.
type BlockParams struct { 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. // EvidenceParams determine how we handle evidence of malfeasance.
type EvidenceParams struct { 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. // ValidatorParams restrict the public key types validators can use.
@ -72,7 +72,7 @@ type ValidatorParams struct {
} }
type VersionParams struct { type VersionParams struct {
AppVersion uint64 `json:"app_version"`
AppVersion uint64 `json:"app_version,string"`
} }
// DefaultConsensusParams returns a default ConsensusParams. // DefaultConsensusParams returns a default ConsensusParams.


+ 37
- 13
types/validator.go View File

@ -17,23 +17,47 @@ import (
// NOTE: The ProposerPriority is not included in Validator.Hash(); // NOTE: The ProposerPriority is not included in Validator.Hash();
// make sure to update that method if changes are made here // make sure to update that method if changes are made here
type Validator struct { 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) { 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. // NewValidator returns a new validator with the given pubkey and voting power.


Loading…
Cancel
Save