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.
This commit is contained in:
M. J. Fromberger
2022-01-14 18:14:09 -08:00
committed by GitHub
parent 7ed57ef5f9
commit dbe2146d0a
19 changed files with 349 additions and 24 deletions

View File

@@ -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"`

View File

@@ -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 --------------------------------------

View File

@@ -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"`

View File

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

View File

@@ -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.

View File

@@ -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"`