From dbe2146d0a01e5986a4fd27e8b2c7461eacaa883 Mon Sep 17 00:00:00 2001 From: "M. J. Fromberger" Date: Fri, 14 Jan 2022 18:14:09 -0800 Subject: [PATCH] rpc: simplify the encoding of interface-typed arguments in JSON (#7600) Add package jsontypes that implements a subset of the custom libs/json package. Specifically it handles encoding and decoding of interface types wrapped in "tagged" JSON objects. It omits the deep reflection on arbitrary types, preserving only the handling of type tags wrapper encoding. - Register interface types (Evidence, PubKey, PrivKey) for tagged encoding. - Update the existing implementations to satisfy the type. - Register those types with the jsontypes registry. - Add string tags to 64-bit integer fields where needed. - Add marshalers to structs that export interface-typed fields. --- crypto/crypto.go | 7 ++ crypto/ed25519/ed25519.go | 10 ++ crypto/secp256k1/secp256k1.go | 10 ++ crypto/sr25519/encoding.go | 8 +- crypto/sr25519/privkey.go | 3 + crypto/sr25519/pubkey.go | 3 + internal/jsontypes/jsontypes.go | 109 ++++++++++++++++++++ internal/jsontypes/jsontypes_test.go | 83 +++++++++++++++ internal/p2p/conn/secret_connection_test.go | 1 + privval/file.go | 17 +++ rpc/client/http/http.go | 4 +- rpc/client/http/request.go | 20 ++++ types/block.go | 4 +- types/evidence.go | 29 ++++-- types/genesis.go | 16 ++- types/node_key.go | 21 +++- types/validator.go | 22 +++- types/vote.go | 2 +- version/version.go | 4 +- 19 files changed, 349 insertions(+), 24 deletions(-) create mode 100644 internal/jsontypes/jsontypes.go create mode 100644 internal/jsontypes/jsontypes_test.go diff --git a/crypto/crypto.go b/crypto/crypto.go index 8d44b82f5..4f0dc05e7 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -2,6 +2,7 @@ package crypto import ( "github.com/tendermint/tendermint/crypto/tmhash" + "github.com/tendermint/tendermint/internal/jsontypes" "github.com/tendermint/tendermint/libs/bytes" ) @@ -25,6 +26,9 @@ type PubKey interface { VerifySignature(msg []byte, sig []byte) bool Equals(PubKey) bool Type() string + + // Implementations must support tagged encoding in JSON. + jsontypes.Tagged } type PrivKey interface { @@ -33,6 +37,9 @@ type PrivKey interface { PubKey() PubKey Equals(PrivKey) bool Type() string + + // Implementations must support tagged encoding in JSON. + jsontypes.Tagged } type Symmetric interface { diff --git a/crypto/ed25519/ed25519.go b/crypto/ed25519/ed25519.go index 3ac7f6d07..8673ff4d5 100644 --- a/crypto/ed25519/ed25519.go +++ b/crypto/ed25519/ed25519.go @@ -12,6 +12,7 @@ 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" ) @@ -58,11 +59,17 @@ const ( func init() { tmjson.RegisterType(PubKey{}, PubKeyName) tmjson.RegisterType(PrivKey{}, PrivKeyName) + + jsontypes.MustRegister(PubKey{}) + jsontypes.MustRegister(PrivKey{}) } // PrivKey implements crypto.PrivKey. type PrivKey []byte +// TypeTag satisfies the jsontypes.Tagged interface. +func (PrivKey) TypeTag() string { return PrivKeyName } + // Bytes returns the privkey byte format. func (privKey PrivKey) Bytes() []byte { return []byte(privKey) @@ -151,6 +158,9 @@ var _ crypto.PubKey = PubKey{} // PubKeyEd25519 implements crypto.PubKey for the Ed25519 signature scheme. type PubKey []byte +// TypeTag satisfies the jsontypes.Tagged interface. +func (PubKey) TypeTag() string { return PubKeyName } + // Address is the SHA256-20 of the raw pubkey bytes. func (pubKey PubKey) Address() crypto.Address { if len(pubKey) != PubKeySize { diff --git a/crypto/secp256k1/secp256k1.go b/crypto/secp256k1/secp256k1.go index c2c0c6017..f92b29c1f 100644 --- a/crypto/secp256k1/secp256k1.go +++ b/crypto/secp256k1/secp256k1.go @@ -10,6 +10,7 @@ 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 @@ -28,6 +29,9 @@ const ( func init() { tmjson.RegisterType(PubKey{}, PubKeyName) tmjson.RegisterType(PrivKey{}, PrivKeyName) + + jsontypes.MustRegister(PubKey{}) + jsontypes.MustRegister(PrivKey{}) } var _ crypto.PrivKey = PrivKey{} @@ -35,6 +39,9 @@ var _ crypto.PrivKey = PrivKey{} // PrivKey implements PrivKey. type PrivKey []byte +// TypeTag satisfies the jsontypes.Tagged interface. +func (PrivKey) TypeTag() string { return PrivKeyName } + // Bytes marshalls the private key using amino encoding. func (privKey PrivKey) Bytes() []byte { return []byte(privKey) @@ -138,6 +145,9 @@ const PubKeySize = 33 // This prefix is followed with the x-coordinate. type PubKey []byte +// TypeTag satisfies the jsontypes.Tagged interface. +func (PubKey) TypeTag() string { return PubKeyName } + // Address returns a Bitcoin style addresses: RIPEMD160(SHA256(pubkey)) func (pubKey PubKey) Address() crypto.Address { if len(pubKey) != PubKeySize { diff --git a/crypto/sr25519/encoding.go b/crypto/sr25519/encoding.go index c0a8a7925..8827ee0b1 100644 --- a/crypto/sr25519/encoding.go +++ b/crypto/sr25519/encoding.go @@ -1,6 +1,9 @@ package sr25519 -import tmjson "github.com/tendermint/tendermint/libs/json" +import ( + "github.com/tendermint/tendermint/internal/jsontypes" + tmjson "github.com/tendermint/tendermint/libs/json" +) const ( PrivKeyName = "tendermint/PrivKeySr25519" @@ -10,4 +13,7 @@ const ( func init() { tmjson.RegisterType(PubKey{}, PubKeyName) tmjson.RegisterType(PrivKey{}, PrivKeyName) + + jsontypes.MustRegister(PubKey{}) + jsontypes.MustRegister(PrivKey{}) } diff --git a/crypto/sr25519/privkey.go b/crypto/sr25519/privkey.go index f628ca1aa..4e9cc995f 100644 --- a/crypto/sr25519/privkey.go +++ b/crypto/sr25519/privkey.go @@ -29,6 +29,9 @@ type PrivKey struct { kp *sr25519.KeyPair } +// TypeTag satisfies the jsontypes.Tagged interface. +func (PrivKey) TypeTag() string { return PrivKeyName } + // Bytes returns the byte-encoded PrivKey. func (privKey PrivKey) Bytes() []byte { if privKey.kp == nil { diff --git a/crypto/sr25519/pubkey.go b/crypto/sr25519/pubkey.go index 7e701dd1f..717f25c8c 100644 --- a/crypto/sr25519/pubkey.go +++ b/crypto/sr25519/pubkey.go @@ -23,6 +23,9 @@ const ( // PubKey implements crypto.PubKey. type PubKey []byte +// TypeTag satisfies the jsontypes.Tagged interface. +func (PubKey) TypeTag() string { return PubKeyName } + // Address is the SHA256-20 of the raw pubkey bytes. func (pubKey PubKey) Address() crypto.Address { if len(pubKey) != PubKeySize { diff --git a/internal/jsontypes/jsontypes.go b/internal/jsontypes/jsontypes.go new file mode 100644 index 000000000..a4c3c53ff --- /dev/null +++ b/internal/jsontypes/jsontypes.go @@ -0,0 +1,109 @@ +// Package jsontypes supports decoding for interface types whose concrete +// implementations need to be stored as JSON. To do this, concrete values are +// packaged in wrapper objects having the form: +// +// { +// "type": "", +// "value": +// } +// +// This package provides a registry for type tag strings and functions to +// encode and decode wrapper objects. +package jsontypes + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" +) + +// The Tagged interface must be implemented by a type in order to register it +// with the jsontypes package. The TypeTag method returns a string label that +// is used to distinguish objects of that type. +type Tagged interface { + TypeTag() string +} + +// registry records the mapping from type tags to value types. Values in this +// map must be normalized to non-pointer types. +var registry = struct { + types map[string]reflect.Type +}{types: make(map[string]reflect.Type)} + +// register adds v to the type registry. It reports an error if the tag +// returned by v is already registered. +func register(v Tagged) error { + tag := v.TypeTag() + if t, ok := registry.types[tag]; ok { + return fmt.Errorf("type tag %q already registered to %v", tag, t) + } + typ := reflect.TypeOf(v) + if typ.Kind() == reflect.Ptr { + typ = typ.Elem() + } + registry.types[tag] = typ + return nil +} + +// MustRegister adds v to the type registry. It will panic if the tag returned +// by v is already registered. This function is meant for use during program +// initialization. +func MustRegister(v Tagged) { + if err := register(v); err != nil { + panic(err) + } +} + +type wrapper struct { + Type string `json:"type"` + Value json.RawMessage `json:"value"` +} + +// Marshal marshals a JSON wrapper object containing v. If v == nil, Marshal +// returns the JSON "null" value without error. +func Marshal(v Tagged) ([]byte, error) { + if v == nil { + return []byte("null"), nil + } + data, err := json.Marshal(v) + if err != nil { + return nil, err + } + return json.Marshal(wrapper{ + Type: v.TypeTag(), + Value: data, + }) +} + +// Unmarshal unmarshals a JSON wrapper object into v. It reports an error if +// the data do not encode a valid wrapper object, if the wrapper's type tag is +// not registered with jsontypes, or if the resulting value is not compatible +// with the type of v. +func Unmarshal(data []byte, v interface{}) error { + // Verify that the target is some kind of pointer. + target := reflect.ValueOf(v) + if target.Kind() != reflect.Ptr { + return fmt.Errorf("target %T is not a pointer", v) + } + + var w wrapper + dec := json.NewDecoder(bytes.NewReader(data)) + dec.DisallowUnknownFields() + if err := dec.Decode(&w); err != nil { + return fmt.Errorf("invalid type wrapper: %w", err) + } + typ, ok := registry.types[w.Type] + if !ok { + return fmt.Errorf("unknown type tag: %q", w.Type) + } else if !typ.AssignableTo(target.Elem().Type()) { + return fmt.Errorf("type %v not assignable to %T", typ, v) + } + + obj := reflect.New(typ) + if err := json.Unmarshal(w.Value, obj.Interface()); err != nil { + return fmt.Errorf("decoding wrapped value: %w", err) + } + target.Elem().Set(obj.Elem()) + return nil +} diff --git a/internal/jsontypes/jsontypes_test.go b/internal/jsontypes/jsontypes_test.go new file mode 100644 index 000000000..b721e2b84 --- /dev/null +++ b/internal/jsontypes/jsontypes_test.go @@ -0,0 +1,83 @@ +package jsontypes_test + +import ( + "testing" + + "github.com/tendermint/tendermint/internal/jsontypes" +) + +type testType struct { + Field string `json:"field"` +} + +func (*testType) TypeTag() string { return "test/TaggedType" } + +func TestRoundTrip(t *testing.T) { + const wantEncoded = `{"type":"test/TaggedType","value":{"field":"hello"}}` + + t.Run("MustRegisterOK", func(t *testing.T) { + defer func() { + if x := recover(); x != nil { + t.Fatalf("Registration panicked: %v", x) + } + }() + jsontypes.MustRegister((*testType)(nil)) + }) + + t.Run("MustRegisterFail", func(t *testing.T) { + defer func() { + if x := recover(); x != nil { + t.Logf("Got expected panic: %v", x) + } + }() + jsontypes.MustRegister((*testType)(nil)) + t.Fatal("Registration should not have succeeded") + }) + + t.Run("MarshalNil", func(t *testing.T) { + bits, err := jsontypes.Marshal(nil) + if err != nil { + t.Fatalf("Marshal failed: %v", err) + } + if got := string(bits); got != "null" { + t.Errorf("Marshal nil: got %#q, want null", got) + } + }) + + t.Run("RoundTrip", func(t *testing.T) { + obj := testType{Field: "hello"} + bits, err := jsontypes.Marshal(&obj) + if err != nil { + t.Fatalf("Marshal %T failed: %v", obj, err) + } + if got := string(bits); got != wantEncoded { + t.Errorf("Marshal %T: got %#q, want %#q", obj, got, wantEncoded) + } + + var cmp testType + if err := jsontypes.Unmarshal(bits, &cmp); err != nil { + t.Errorf("Unmarshal %#q failed: %v", string(bits), err) + } + if obj != cmp { + t.Errorf("Unmarshal %#q: got %+v, want %+v", string(bits), cmp, obj) + } + }) + + t.Run("Unregistered", func(t *testing.T) { + obj := testType{Field: "hello"} + bits, err := jsontypes.Marshal(&obj) + if err != nil { + t.Fatalf("Marshal %T failed: %v", obj, err) + } + if got := string(bits); got != wantEncoded { + t.Errorf("Marshal %T: got %#q, want %#q", obj, got, wantEncoded) + } + + var cmp struct { + Field string `json:"field"` + } + if err := jsontypes.Unmarshal(bits, &cmp); err != nil { + t.Errorf("Unmarshal %#q: got %+v, want %+v", string(bits), cmp, obj) + } + }) +} diff --git a/internal/p2p/conn/secret_connection_test.go b/internal/p2p/conn/secret_connection_test.go index 7bc5e0b34..362c8102f 100644 --- a/internal/p2p/conn/secret_connection_test.go +++ b/internal/p2p/conn/secret_connection_test.go @@ -52,6 +52,7 @@ func (pk privKeyWithNilPubKey) Sign(msg []byte) ([]byte, error) { return pk.orig func (pk privKeyWithNilPubKey) PubKey() crypto.PubKey { return nil } func (pk privKeyWithNilPubKey) Equals(pk2 crypto.PrivKey) bool { return pk.orig.Equals(pk2) } func (pk privKeyWithNilPubKey) Type() string { return "privKeyWithNilPubKey" } +func (privKeyWithNilPubKey) TypeTag() string { return "test/privKeyWithNilPubKey" } func TestSecretConnectionHandshake(t *testing.T) { fooSecConn, barSecConn := makeSecretConnPair(t) diff --git a/privval/file.go b/privval/file.go index a075323a8..b11346dc7 100644 --- a/privval/file.go +++ b/privval/file.go @@ -14,6 +14,7 @@ import ( "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/secp256k1" + "github.com/tendermint/tendermint/internal/jsontypes" "github.com/tendermint/tendermint/internal/libs/protoio" "github.com/tendermint/tendermint/internal/libs/tempfile" tmbytes "github.com/tendermint/tendermint/libs/bytes" @@ -55,6 +56,22 @@ type FilePVKey struct { filePath string } +func (pvKey FilePVKey) MarshalJSON() ([]byte, error) { + pubk, err := jsontypes.Marshal(pvKey.PubKey) + if err != nil { + return nil, err + } + privk, err := jsontypes.Marshal(pvKey.PrivKey) + 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}) +} + // Save persists the FilePVKey to its filePath. func (pvKey FilePVKey) Save() error { outFile := pvKey.filePath diff --git a/rpc/client/http/http.go b/rpc/client/http/http.go index 7dd6abb76..f10985ae3 100644 --- a/rpc/client/http/http.go +++ b/rpc/client/http/http.go @@ -510,7 +510,9 @@ func (c *baseRPCClient) BroadcastEvidence( ev types.Evidence, ) (*coretypes.ResultBroadcastEvidence, error) { result := new(coretypes.ResultBroadcastEvidence) - if err := c.caller.Call(ctx, "broadcast_evidence", map[string]interface{}{"evidence": ev}, result); err != nil { + if err := c.caller.Call(ctx, "broadcast_evidence", evidenceArgs{ + Evidence: ev, + }, result); err != nil { return nil, err } return result, nil diff --git a/rpc/client/http/request.go b/rpc/client/http/request.go index 5d1d3db5b..a6f85b637 100644 --- a/rpc/client/http/request.go +++ b/rpc/client/http/request.go @@ -4,7 +4,11 @@ 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" ) type abciQueryArgs struct { @@ -57,3 +61,19 @@ type validatorArgs struct { Page *int `json:"page,string,omitempty"` PerPage *int `json:"per_page,string,omitempty"` } + +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}) +} diff --git a/types/block.go b/types/block.go index e09d1830a..241c360de 100644 --- a/types/block.go +++ b/types/block.go @@ -334,7 +334,7 @@ type Header struct { // basic block info Version version.Consensus `json:"version"` ChainID string `json:"chain_id"` - Height int64 `json:"height"` + Height int64 `json:"height,string"` Time time.Time `json:"time"` // prev block info @@ -748,7 +748,7 @@ type Commit struct { // ValidatorSet order. // Any peer with a block can gossip signatures by index with a peer without // recalculating the active ValidatorSet. - Height int64 `json:"height"` + Height int64 `json:"height,string"` Round int32 `json:"round"` BlockID BlockID `json:"block_id"` Signatures []CommitSig `json:"signatures"` diff --git a/types/evidence.go b/types/evidence.go index fa530761f..cca6fc899 100644 --- a/types/evidence.go +++ b/types/evidence.go @@ -13,6 +13,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "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" @@ -28,6 +29,9 @@ type Evidence interface { String() string // string format of the evidence Time() time.Time // time of the infraction ValidateBasic() error // basic consistency check + + // Implementations must support tagged encoding in JSON. + jsontypes.Tagged } //-------------------------------------------------------------------------------------- @@ -38,11 +42,14 @@ type DuplicateVoteEvidence struct { VoteB *Vote `json:"vote_b"` // abci specific information - TotalVotingPower int64 - ValidatorPower int64 + TotalVotingPower int64 `json:",string"` + ValidatorPower int64 `json:",string"` Timestamp time.Time } +// TypeTag implements the jsontypes.Tagged interface. +func (*DuplicateVoteEvidence) TypeTag() string { return "tendermint/DuplicateVoteEvidence" } + var _ Evidence = &DuplicateVoteEvidence{} // NewDuplicateVoteEvidence creates DuplicateVoteEvidence with right ordering given @@ -236,14 +243,17 @@ func DuplicateVoteEvidenceFromProto(pb *tmproto.DuplicateVoteEvidence) (*Duplica // height, then nodes will treat this as of the Lunatic form, else it is of the Equivocation form. type LightClientAttackEvidence struct { ConflictingBlock *LightBlock - CommonHeight int64 + CommonHeight int64 `json:",string"` // abci specific information ByzantineValidators []*Validator // validators in the validator set that misbehaved in creating the conflicting block - TotalVotingPower int64 // total voting power of the validator set at the common height + TotalVotingPower int64 `json:",string"` // total voting power of the validator set at the common height Timestamp time.Time // timestamp of the block at the common height } +// TypeTag implements the jsontypes.Tagged interface. +func (*LightClientAttackEvidence) TypeTag() string { return "tendermint/LightClientAttackEvidence" } + var _ Evidence = &LightClientAttackEvidence{} // ABCI forms an array of abci evidence for each byzantine validator @@ -365,10 +375,10 @@ func (l *LightClientAttackEvidence) Height() int64 { // String returns a string representation of LightClientAttackEvidence func (l *LightClientAttackEvidence) String() string { return fmt.Sprintf(`LightClientAttackEvidence{ - ConflictingBlock: %v, - CommonHeight: %d, - ByzatineValidators: %v, - TotalVotingPower: %d, + ConflictingBlock: %v, + CommonHeight: %d, + ByzatineValidators: %v, + TotalVotingPower: %d, Timestamp: %v}#%X`, l.ConflictingBlock.String(), l.CommonHeight, l.ByzantineValidators, l.TotalVotingPower, l.Timestamp, l.Hash()) @@ -630,6 +640,9 @@ 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)) } //-------------------------------------------- ERRORS -------------------------------------- diff --git a/types/genesis.go b/types/genesis.go index a4b3904ab..d89de6ea2 100644 --- a/types/genesis.go +++ b/types/genesis.go @@ -9,6 +9,7 @@ import ( "time" "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" @@ -29,10 +30,23 @@ const ( type GenesisValidator struct { Address Address `json:"address"` PubKey crypto.PubKey `json:"pub_key"` - Power int64 `json:"power"` + Power int64 `json:"power,string"` Name string `json:"name"` } +func (g GenesisValidator) MarshalJSON() ([]byte, error) { + pk, err := jsontypes.Marshal(g.PubKey) + 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}) +} + // GenesisDoc defines the initial conditions for a tendermint blockchain, in particular its validator set. type GenesisDoc struct { GenesisTime time.Time `json:"genesis_time"` diff --git a/types/node_key.go b/types/node_key.go index aecbd8a21..8f59b6085 100644 --- a/types/node_key.go +++ b/types/node_key.go @@ -1,10 +1,12 @@ package types import ( + "encoding/json" "os" "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" ) @@ -22,14 +24,25 @@ type NodeKey struct { PrivKey crypto.PrivKey `json:"priv_key"` } +func (nk NodeKey) MarshalJSON() ([]byte, error) { + pk, err := jsontypes.Marshal(nk.PrivKey) + 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}) +} + // PubKey returns the peer's PubKey -func (nodeKey NodeKey) PubKey() crypto.PubKey { - return nodeKey.PrivKey.PubKey() +func (nk NodeKey) PubKey() crypto.PubKey { + return nk.PrivKey.PubKey() } // SaveAs persists the NodeKey to filePath. -func (nodeKey NodeKey) SaveAs(filePath string) error { - jsonBytes, err := tmjson.Marshal(nodeKey) +func (nk NodeKey) SaveAs(filePath string) error { + jsonBytes, err := tmjson.Marshal(nk) if err != nil { return err } diff --git a/types/validator.go b/types/validator.go index ded8156bf..9d48c93e7 100644 --- a/types/validator.go +++ b/types/validator.go @@ -2,12 +2,14 @@ package types import ( "bytes" + "encoding/json" "errors" "fmt" "strings" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/encoding" + "github.com/tendermint/tendermint/internal/jsontypes" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" ) @@ -15,11 +17,23 @@ 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"` + Address Address `json:"address"` + PubKey crypto.PubKey `json:"pub_key"` + VotingPower int64 `json:"voting_power,string"` + ProposerPriority int64 `json:"proposer_priority,string"` +} - ProposerPriority int64 `json:"proposer_priority"` +func (v Validator) MarshalJSON() ([]byte, error) { + pk, err := jsontypes.Marshal(v.PubKey) + if err != nil { + return nil, err + } + 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}) } // NewValidator returns a new validator with the given pubkey and voting power. diff --git a/types/vote.go b/types/vote.go index 52fb73ec5..ceae65e48 100644 --- a/types/vote.go +++ b/types/vote.go @@ -49,7 +49,7 @@ type Address = crypto.Address // consensus. type Vote struct { Type tmproto.SignedMsgType `json:"type"` - Height int64 `json:"height"` + Height int64 `json:"height,string"` Round int32 `json:"round"` // assume there will not be greater than 2_147_483_647 rounds BlockID BlockID `json:"block_id"` // zero if vote is nil. Timestamp time.Time `json:"timestamp"` diff --git a/version/version.go b/version/version.go index e42952f77..483fca031 100644 --- a/version/version.go +++ b/version/version.go @@ -28,8 +28,8 @@ var ( ) type Consensus struct { - Block uint64 `json:"block"` - App uint64 `json:"app"` + Block uint64 `json:"block,string"` + App uint64 `json:"app,string"` } func (c Consensus) ToProto() tmversion.Consensus {