mirror of
https://github.com/tendermint/tendermint.git
synced 2026-07-20 15:02:33 +00:00
Merge remote-tracking branch 'origin/master' into wb/pbts-rebase-master
This commit is contained in:
@@ -49,7 +49,7 @@ jobs:
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Publish to Docker Hub
|
||||
uses: docker/build-push-action@v2.7.0
|
||||
uses: docker/build-push-action@v2.8.0
|
||||
with:
|
||||
context: .
|
||||
file: ./DOCKER/Dockerfile
|
||||
|
||||
@@ -39,7 +39,8 @@ Special thanks to external contributors on this release:
|
||||
- [p2p] \#7064 Remove WDRR queue implementation. (@tychoish)
|
||||
- [config] \#7169 `WriteConfigFile` now returns an error. (@tychoish)
|
||||
- [libs/service] \#7288 Remove SetLogger method on `service.Service` interface. (@tychoish)
|
||||
|
||||
- [abci/client] \#7607 Simplify client interface (removes most "async" methods). (@creachadair)
|
||||
- [libs/json] \#7673 Remove the libs/json (tmjson) library. (@creachadair)
|
||||
|
||||
- Blockchain Protocol
|
||||
|
||||
|
||||
@@ -308,4 +308,4 @@ $(BUILDDIR)/packages.txt:$(GO_TEST_FILES) $(BUILDDIR)
|
||||
split-test-packages:$(BUILDDIR)/packages.txt
|
||||
split -d -n l/$(NUM_SPLIT) $< $<.
|
||||
test-group-%:split-test-packages
|
||||
cat $(BUILDDIR)/packages.txt.$* | xargs go test -mod=readonly -timeout=8m -race -coverprofile=$(BUILDDIR)/$*.profile.out
|
||||
cat $(BUILDDIR)/packages.txt.$* | xargs go test -mod=readonly -timeout=5m -race -coverprofile=$(BUILDDIR)/$*.profile.out
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -20,7 +20,7 @@ var GenNodeKeyCmd = &cobra.Command{
|
||||
func genNodeKey(cmd *cobra.Command, args []string) error {
|
||||
nodeKey := types.GenNodeKey()
|
||||
|
||||
bz, err := tmjson.Marshal(nodeKey)
|
||||
bz, err := json.Marshal(nodeKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("nodeKey -> json: %w", err)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
"github.com/tendermint/tendermint/privval"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
@@ -29,7 +29,7 @@ func genValidator(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
jsbz, err := tmjson.Marshal(pv)
|
||||
jsbz, err := json.Marshal(pv)
|
||||
if err != nil {
|
||||
return fmt.Errorf("validator -> json: %w", err)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
"github.com/tendermint/tendermint/internal/jsontypes"
|
||||
tmnet "github.com/tendermint/tendermint/libs/net"
|
||||
tmos "github.com/tendermint/tendermint/libs/os"
|
||||
"github.com/tendermint/tendermint/privval"
|
||||
@@ -70,7 +70,7 @@ func showValidator(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
}
|
||||
|
||||
bz, err := tmjson.Marshal(pubKey)
|
||||
bz, err := jsontypes.Marshal(pubKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal private validator pubkey: %w", err)
|
||||
}
|
||||
|
||||
+2
-2
@@ -2,6 +2,7 @@ package config
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -9,7 +10,6 @@ import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmos "github.com/tendermint/tendermint/libs/os"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
@@ -270,7 +270,7 @@ func (cfg BaseConfig) LoadNodeKeyID() (types.NodeID, error) {
|
||||
return "", err
|
||||
}
|
||||
nodeKey := types.NodeKey{}
|
||||
err = tmjson.Unmarshal(jsonBytes, &nodeKey)
|
||||
err = json.Unmarshal(jsonBytes, &nodeKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
"github.com/tendermint/tendermint/internal/jsontypes"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
)
|
||||
|
||||
//-------------------------------------
|
||||
@@ -57,9 +56,6 @@ const (
|
||||
)
|
||||
|
||||
func init() {
|
||||
tmjson.RegisterType(PubKey{}, PubKeyName)
|
||||
tmjson.RegisterType(PrivKey{}, PrivKeyName)
|
||||
|
||||
jsontypes.MustRegister(PubKey{})
|
||||
jsontypes.MustRegister(PrivKey{})
|
||||
}
|
||||
|
||||
@@ -7,14 +7,14 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
"github.com/tendermint/tendermint/crypto/sr25519"
|
||||
"github.com/tendermint/tendermint/libs/json"
|
||||
"github.com/tendermint/tendermint/internal/jsontypes"
|
||||
cryptoproto "github.com/tendermint/tendermint/proto/tendermint/crypto"
|
||||
)
|
||||
|
||||
func init() {
|
||||
json.RegisterType((*cryptoproto.PublicKey)(nil), "tendermint.crypto.PublicKey")
|
||||
json.RegisterType((*cryptoproto.PublicKey_Ed25519)(nil), "tendermint.crypto.PublicKey_Ed25519")
|
||||
json.RegisterType((*cryptoproto.PublicKey_Secp256K1)(nil), "tendermint.crypto.PublicKey_Secp256K1")
|
||||
jsontypes.MustRegister((*cryptoproto.PublicKey)(nil))
|
||||
jsontypes.MustRegister((*cryptoproto.PublicKey_Ed25519)(nil))
|
||||
jsontypes.MustRegister((*cryptoproto.PublicKey_Secp256K1)(nil))
|
||||
}
|
||||
|
||||
// PubKeyToProto takes crypto.PubKey and transforms it to a protobuf Pubkey
|
||||
|
||||
@@ -24,10 +24,10 @@ const (
|
||||
// everything. This also affects the generalized proof system as
|
||||
// well.
|
||||
type Proof struct {
|
||||
Total int64 `json:"total"` // Total number of items.
|
||||
Index int64 `json:"index"` // Index of item to prove.
|
||||
LeafHash []byte `json:"leaf_hash"` // Hash of item value.
|
||||
Aunts [][]byte `json:"aunts"` // Hashes from leaf's sibling to a root's child.
|
||||
Total int64 `json:"total,string"` // Total number of items.
|
||||
Index int64 `json:"index,string"` // Index of item to prove.
|
||||
LeafHash []byte `json:"leaf_hash"` // Hash of item value.
|
||||
Aunts [][]byte `json:"aunts"` // Hashes from leaf's sibling to a root's child.
|
||||
}
|
||||
|
||||
// ProofsFromByteSlices computes inclusion proof for given items.
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
secp256k1 "github.com/btcsuite/btcd/btcec"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/internal/jsontypes"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
|
||||
// necessary for Bitcoin address format
|
||||
"golang.org/x/crypto/ripemd160" // nolint
|
||||
@@ -27,9 +26,6 @@ const (
|
||||
)
|
||||
|
||||
func init() {
|
||||
tmjson.RegisterType(PubKey{}, PubKeyName)
|
||||
tmjson.RegisterType(PrivKey{}, PrivKeyName)
|
||||
|
||||
jsontypes.MustRegister(PubKey{})
|
||||
jsontypes.MustRegister(PrivKey{})
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package sr25519
|
||||
|
||||
import (
|
||||
"github.com/tendermint/tendermint/internal/jsontypes"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -11,9 +10,6 @@ const (
|
||||
)
|
||||
|
||||
func init() {
|
||||
tmjson.RegisterType(PubKey{}, PubKeyName)
|
||||
tmjson.RegisterType(PrivKey{}, PrivKeyName)
|
||||
|
||||
jsontypes.MustRegister(PubKey{})
|
||||
jsontypes.MustRegister(PrivKey{})
|
||||
}
|
||||
|
||||
Generated
+1912
-1390
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -4,7 +4,7 @@
|
||||
"description": "Tendermint Core Documentation",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"vuepress-theme-cosmos": "^1.0.182"
|
||||
"vuepress-theme-cosmos": "^1.0.183"
|
||||
},
|
||||
"devDependencies": {
|
||||
"watchpack": "^2.3.1"
|
||||
|
||||
+38
-18
@@ -5,8 +5,8 @@ import (
|
||||
"fmt"
|
||||
|
||||
cstypes "github.com/tendermint/tendermint/internal/consensus/types"
|
||||
"github.com/tendermint/tendermint/internal/jsontypes"
|
||||
"github.com/tendermint/tendermint/libs/bits"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
tmmath "github.com/tendermint/tendermint/libs/math"
|
||||
tmcons "github.com/tendermint/tendermint/proto/tendermint/consensus"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
@@ -18,30 +18,34 @@ import (
|
||||
// converted to a Message via MsgFromProto.
|
||||
type Message interface {
|
||||
ValidateBasic() error
|
||||
|
||||
jsontypes.Tagged
|
||||
}
|
||||
|
||||
func init() {
|
||||
tmjson.RegisterType(&NewRoundStepMessage{}, "tendermint/NewRoundStepMessage")
|
||||
tmjson.RegisterType(&NewValidBlockMessage{}, "tendermint/NewValidBlockMessage")
|
||||
tmjson.RegisterType(&ProposalMessage{}, "tendermint/Proposal")
|
||||
tmjson.RegisterType(&ProposalPOLMessage{}, "tendermint/ProposalPOL")
|
||||
tmjson.RegisterType(&BlockPartMessage{}, "tendermint/BlockPart")
|
||||
tmjson.RegisterType(&VoteMessage{}, "tendermint/Vote")
|
||||
tmjson.RegisterType(&HasVoteMessage{}, "tendermint/HasVote")
|
||||
tmjson.RegisterType(&VoteSetMaj23Message{}, "tendermint/VoteSetMaj23")
|
||||
tmjson.RegisterType(&VoteSetBitsMessage{}, "tendermint/VoteSetBits")
|
||||
jsontypes.MustRegister(&NewRoundStepMessage{})
|
||||
jsontypes.MustRegister(&NewValidBlockMessage{})
|
||||
jsontypes.MustRegister(&ProposalMessage{})
|
||||
jsontypes.MustRegister(&ProposalPOLMessage{})
|
||||
jsontypes.MustRegister(&BlockPartMessage{})
|
||||
jsontypes.MustRegister(&VoteMessage{})
|
||||
jsontypes.MustRegister(&HasVoteMessage{})
|
||||
jsontypes.MustRegister(&VoteSetMaj23Message{})
|
||||
jsontypes.MustRegister(&VoteSetBitsMessage{})
|
||||
}
|
||||
|
||||
// NewRoundStepMessage is sent for every step taken in the ConsensusState.
|
||||
// For every height/round/step transition
|
||||
type NewRoundStepMessage struct {
|
||||
Height int64
|
||||
Height int64 `json:",string"`
|
||||
Round int32
|
||||
Step cstypes.RoundStepType
|
||||
SecondsSinceStartTime int64
|
||||
SecondsSinceStartTime int64 `json:",string"`
|
||||
LastCommitRound int32
|
||||
}
|
||||
|
||||
func (*NewRoundStepMessage) TypeTag() string { return "tendermint/NewRoundStepMessage" }
|
||||
|
||||
// ValidateBasic performs basic validation.
|
||||
func (m *NewRoundStepMessage) ValidateBasic() error {
|
||||
if m.Height < 0 {
|
||||
@@ -93,13 +97,15 @@ func (m *NewRoundStepMessage) String() string {
|
||||
// i.e., there is a Proposal for block B and 2/3+ prevotes for the block B in the round r.
|
||||
// In case the block is also committed, then IsCommit flag is set to true.
|
||||
type NewValidBlockMessage struct {
|
||||
Height int64
|
||||
Height int64 `json:",string"`
|
||||
Round int32
|
||||
BlockPartSetHeader types.PartSetHeader
|
||||
BlockParts *bits.BitArray
|
||||
IsCommit bool
|
||||
}
|
||||
|
||||
func (*NewValidBlockMessage) TypeTag() string { return "tendermint/NewValidBlockMessage" }
|
||||
|
||||
// ValidateBasic performs basic validation.
|
||||
func (m *NewValidBlockMessage) ValidateBasic() error {
|
||||
if m.Height < 0 {
|
||||
@@ -136,6 +142,8 @@ type ProposalMessage struct {
|
||||
Proposal *types.Proposal
|
||||
}
|
||||
|
||||
func (*ProposalMessage) TypeTag() string { return "tendermint/Proposal" }
|
||||
|
||||
// ValidateBasic performs basic validation.
|
||||
func (m *ProposalMessage) ValidateBasic() error {
|
||||
return m.Proposal.ValidateBasic()
|
||||
@@ -148,11 +156,13 @@ func (m *ProposalMessage) String() string {
|
||||
|
||||
// ProposalPOLMessage is sent when a previous proposal is re-proposed.
|
||||
type ProposalPOLMessage struct {
|
||||
Height int64
|
||||
Height int64 `json:",string"`
|
||||
ProposalPOLRound int32
|
||||
ProposalPOL *bits.BitArray
|
||||
}
|
||||
|
||||
func (*ProposalPOLMessage) TypeTag() string { return "tendermint/ProposalPOL" }
|
||||
|
||||
// ValidateBasic performs basic validation.
|
||||
func (m *ProposalPOLMessage) ValidateBasic() error {
|
||||
if m.Height < 0 {
|
||||
@@ -177,11 +187,13 @@ func (m *ProposalPOLMessage) String() string {
|
||||
|
||||
// BlockPartMessage is sent when gossipping a piece of the proposed block.
|
||||
type BlockPartMessage struct {
|
||||
Height int64
|
||||
Height int64 `json:",string"`
|
||||
Round int32
|
||||
Part *types.Part
|
||||
}
|
||||
|
||||
func (*BlockPartMessage) TypeTag() string { return "tendermint/BlockPart" }
|
||||
|
||||
// ValidateBasic performs basic validation.
|
||||
func (m *BlockPartMessage) ValidateBasic() error {
|
||||
if m.Height < 0 {
|
||||
@@ -206,6 +218,8 @@ type VoteMessage struct {
|
||||
Vote *types.Vote
|
||||
}
|
||||
|
||||
func (*VoteMessage) TypeTag() string { return "tendermint/Vote" }
|
||||
|
||||
// ValidateBasic performs basic validation.
|
||||
func (m *VoteMessage) ValidateBasic() error {
|
||||
return m.Vote.ValidateBasic()
|
||||
@@ -218,12 +232,14 @@ func (m *VoteMessage) String() string {
|
||||
|
||||
// HasVoteMessage is sent to indicate that a particular vote has been received.
|
||||
type HasVoteMessage struct {
|
||||
Height int64
|
||||
Height int64 `json:",string"`
|
||||
Round int32
|
||||
Type tmproto.SignedMsgType
|
||||
Index int32
|
||||
}
|
||||
|
||||
func (*HasVoteMessage) TypeTag() string { return "tendermint/HasVote" }
|
||||
|
||||
// ValidateBasic performs basic validation.
|
||||
func (m *HasVoteMessage) ValidateBasic() error {
|
||||
if m.Height < 0 {
|
||||
@@ -248,12 +264,14 @@ func (m *HasVoteMessage) String() string {
|
||||
|
||||
// VoteSetMaj23Message is sent to indicate that a given BlockID has seen +2/3 votes.
|
||||
type VoteSetMaj23Message struct {
|
||||
Height int64
|
||||
Height int64 `json:",string"`
|
||||
Round int32
|
||||
Type tmproto.SignedMsgType
|
||||
BlockID types.BlockID
|
||||
}
|
||||
|
||||
func (*VoteSetMaj23Message) TypeTag() string { return "tendermint/VoteSetMaj23" }
|
||||
|
||||
// ValidateBasic performs basic validation.
|
||||
func (m *VoteSetMaj23Message) ValidateBasic() error {
|
||||
if m.Height < 0 {
|
||||
@@ -280,13 +298,15 @@ func (m *VoteSetMaj23Message) String() string {
|
||||
// VoteSetBitsMessage is sent to communicate the bit-array of votes seen for the
|
||||
// BlockID.
|
||||
type VoteSetBitsMessage struct {
|
||||
Height int64
|
||||
Height int64 `json:",string"`
|
||||
Round int32
|
||||
Type tmproto.SignedMsgType
|
||||
BlockID types.BlockID
|
||||
Votes *bits.BitArray
|
||||
}
|
||||
|
||||
func (*VoteSetBitsMessage) TypeTag() string { return "tendermint/VoteSetBits" }
|
||||
|
||||
// ValidateBasic performs basic validation.
|
||||
func (m *VoteSetBitsMessage) ValidateBasic() error {
|
||||
if m.Height < 0 {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
@@ -9,7 +10,6 @@ import (
|
||||
cstypes "github.com/tendermint/tendermint/internal/consensus/types"
|
||||
tmsync "github.com/tendermint/tendermint/internal/libs/sync"
|
||||
"github.com/tendermint/tendermint/libs/bits"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmtime "github.com/tendermint/tendermint/libs/time"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
@@ -96,8 +96,7 @@ func (ps *PeerState) GetRoundState() *cstypes.PeerRoundState {
|
||||
func (ps *PeerState) ToJSON() ([]byte, error) {
|
||||
ps.mtx.Lock()
|
||||
defer ps.mtx.Unlock()
|
||||
|
||||
return tmjson.Marshal(ps)
|
||||
return json.Marshal(ps)
|
||||
}
|
||||
|
||||
// GetHeight returns an atomic snapshot of the PeerRoundState's height used by
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
cstypes "github.com/tendermint/tendermint/internal/consensus/types"
|
||||
"github.com/tendermint/tendermint/internal/eventbus"
|
||||
"github.com/tendermint/tendermint/internal/jsontypes"
|
||||
sm "github.com/tendermint/tendermint/internal/state"
|
||||
tmevents "github.com/tendermint/tendermint/libs/events"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
@@ -46,19 +47,49 @@ var msgQueueSize = 1000
|
||||
|
||||
// msgs from the reactor which may update the state
|
||||
type msgInfo struct {
|
||||
Msg Message `json:"msg"`
|
||||
PeerID types.NodeID `json:"peer_key"`
|
||||
ReceiveTime time.Time `json:"receive_time"`
|
||||
Msg Message
|
||||
PeerID types.NodeID
|
||||
ReceiveTime time.Time
|
||||
}
|
||||
|
||||
func (msgInfo) TypeTag() string { return "tendermint/wal/MsgInfo" }
|
||||
|
||||
type msgInfoJSON struct {
|
||||
Msg json.RawMessage `json:"msg"`
|
||||
PeerID types.NodeID `json:"peer_key"`
|
||||
ReceiveTime time.Time `json:"receive_time"`
|
||||
}
|
||||
|
||||
func (m msgInfo) MarshalJSON() ([]byte, error) {
|
||||
msg, err := jsontypes.Marshal(m.Msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(msgInfoJSON{Msg: msg, PeerID: m.PeerID, ReceiveTime: m.ReceiveTime})
|
||||
}
|
||||
|
||||
func (m *msgInfo) UnmarshalJSON(data []byte) error {
|
||||
var msg msgInfoJSON
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := jsontypes.Unmarshal(msg.Msg, &m.Msg); err != nil {
|
||||
return err
|
||||
}
|
||||
m.PeerID = msg.PeerID
|
||||
return nil
|
||||
}
|
||||
|
||||
// internally generated messages which may update the state
|
||||
type timeoutInfo struct {
|
||||
Duration time.Duration `json:"duration"`
|
||||
Height int64 `json:"height"`
|
||||
Duration time.Duration `json:"duration,string"`
|
||||
Height int64 `json:"height,string"`
|
||||
Round int32 `json:"round"`
|
||||
Step cstypes.RoundStepType `json:"step"`
|
||||
}
|
||||
|
||||
func (timeoutInfo) TypeTag() string { return "tendermint/wal/TimeoutInfo" }
|
||||
|
||||
func (ti *timeoutInfo) String() string {
|
||||
return fmt.Sprintf("%v ; %d/%d %v", ti.Duration, ti.Height, ti.Round, ti.Step)
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ func (rs RoundStepType) String() string {
|
||||
// NOTE: Not thread safe. Should only be manipulated by functions downstream
|
||||
// of the cs.receiveRoutine
|
||||
type RoundState struct {
|
||||
Height int64 `json:"height"` // Height we are working on
|
||||
Height int64 `json:"height,string"` // Height we are working on
|
||||
Round int32 `json:"round"`
|
||||
Step RoundStepType `json:"step"`
|
||||
StartTime time.Time `json:"start_time"`
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
|
||||
"github.com/tendermint/tendermint/internal/jsontypes"
|
||||
auto "github.com/tendermint/tendermint/internal/libs/autofile"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmos "github.com/tendermint/tendermint/libs/os"
|
||||
"github.com/tendermint/tendermint/libs/service"
|
||||
@@ -41,15 +41,17 @@ type TimedWALMessage struct {
|
||||
// EndHeightMessage marks the end of the given height inside WAL.
|
||||
// @internal used by scripts/wal2json util.
|
||||
type EndHeightMessage struct {
|
||||
Height int64 `json:"height"`
|
||||
Height int64 `json:"height,string"`
|
||||
}
|
||||
|
||||
func (EndHeightMessage) TypeTag() string { return "tendermint/wal/EndHeightMessage" }
|
||||
|
||||
type WALMessage interface{}
|
||||
|
||||
func init() {
|
||||
tmjson.RegisterType(msgInfo{}, "tendermint/wal/MsgInfo")
|
||||
tmjson.RegisterType(timeoutInfo{}, "tendermint/wal/TimeoutInfo")
|
||||
tmjson.RegisterType(EndHeightMessage{}, "tendermint/wal/EndHeightMessage")
|
||||
jsontypes.MustRegister(msgInfo{})
|
||||
jsontypes.MustRegister(timeoutInfo{})
|
||||
jsontypes.MustRegister(EndHeightMessage{})
|
||||
}
|
||||
|
||||
//--------------------------------------------------------
|
||||
|
||||
@@ -25,8 +25,7 @@ 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.
|
||||
// registry records the mapping from type tags to value types.
|
||||
var registry = struct {
|
||||
types map[string]reflect.Type
|
||||
}{types: make(map[string]reflect.Type)}
|
||||
@@ -38,11 +37,7 @@ func register(v Tagged) error {
|
||||
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
|
||||
registry.types[tag] = reflect.TypeOf(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -85,6 +80,13 @@ func Unmarshal(data []byte, v interface{}) error {
|
||||
target := reflect.ValueOf(v)
|
||||
if target.Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("target %T is not a pointer", v)
|
||||
} else if target.IsZero() {
|
||||
return fmt.Errorf("target is a nil %T", v)
|
||||
}
|
||||
baseType := target.Type().Elem()
|
||||
if isNull(data) {
|
||||
target.Elem().Set(reflect.Zero(baseType))
|
||||
return nil
|
||||
}
|
||||
|
||||
var w wrapper
|
||||
@@ -95,15 +97,25 @@ func Unmarshal(data []byte, v interface{}) error {
|
||||
}
|
||||
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)
|
||||
return fmt.Errorf("unknown type tag for %T: %q", v, w.Type)
|
||||
}
|
||||
|
||||
obj := reflect.New(typ)
|
||||
if typ.AssignableTo(baseType) {
|
||||
// ok: registered type is directly assignable to the target
|
||||
} else if typ.Kind() == reflect.Ptr && typ.Elem().AssignableTo(baseType) {
|
||||
typ = typ.Elem()
|
||||
// ok: registered type is a pointer to a value assignable to the target
|
||||
} else {
|
||||
return fmt.Errorf("type %v is not assignable to %v", typ, baseType)
|
||||
}
|
||||
obj := reflect.New(typ) // we need a pointer to unmarshal
|
||||
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
|
||||
}
|
||||
|
||||
// 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"))
|
||||
}
|
||||
|
||||
@@ -6,35 +6,44 @@ import (
|
||||
"github.com/tendermint/tendermint/internal/jsontypes"
|
||||
)
|
||||
|
||||
type testType struct {
|
||||
type testPtrType struct {
|
||||
Field string `json:"field"`
|
||||
}
|
||||
|
||||
func (*testType) TypeTag() string { return "test/TaggedType" }
|
||||
func (*testPtrType) TypeTag() string { return "test/PointerType" }
|
||||
func (t *testPtrType) Value() string { return t.Field }
|
||||
|
||||
type testBareType struct {
|
||||
Field string `json:"field"`
|
||||
}
|
||||
|
||||
func (testBareType) TypeTag() string { return "test/BareType" }
|
||||
func (t testBareType) Value() string { return t.Field }
|
||||
|
||||
type fielder interface{ Value() string }
|
||||
|
||||
func TestRoundTrip(t *testing.T) {
|
||||
const wantEncoded = `{"type":"test/TaggedType","value":{"field":"hello"}}`
|
||||
|
||||
t.Run("MustRegisterOK", func(t *testing.T) {
|
||||
t.Run("MustRegister_ok", func(t *testing.T) {
|
||||
defer func() {
|
||||
if x := recover(); x != nil {
|
||||
t.Fatalf("Registration panicked: %v", x)
|
||||
}
|
||||
}()
|
||||
jsontypes.MustRegister((*testType)(nil))
|
||||
jsontypes.MustRegister((*testPtrType)(nil))
|
||||
jsontypes.MustRegister(testBareType{})
|
||||
})
|
||||
|
||||
t.Run("MustRegisterFail", func(t *testing.T) {
|
||||
t.Run("MustRegister_fail", func(t *testing.T) {
|
||||
defer func() {
|
||||
if x := recover(); x != nil {
|
||||
t.Logf("Got expected panic: %v", x)
|
||||
}
|
||||
}()
|
||||
jsontypes.MustRegister((*testType)(nil))
|
||||
jsontypes.MustRegister((*testPtrType)(nil))
|
||||
t.Fatal("Registration should not have succeeded")
|
||||
})
|
||||
|
||||
t.Run("MarshalNil", func(t *testing.T) {
|
||||
t.Run("Marshal_nilTagged", func(t *testing.T) {
|
||||
bits, err := jsontypes.Marshal(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal failed: %v", err)
|
||||
@@ -44,8 +53,10 @@ func TestRoundTrip(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("RoundTrip", func(t *testing.T) {
|
||||
obj := testType{Field: "hello"}
|
||||
t.Run("RoundTrip_pointerType", func(t *testing.T) {
|
||||
const wantEncoded = `{"type":"test/PointerType","value":{"field":"hello"}}`
|
||||
|
||||
obj := testPtrType{Field: "hello"}
|
||||
bits, err := jsontypes.Marshal(&obj)
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal %T failed: %v", obj, err)
|
||||
@@ -54,7 +65,7 @@ func TestRoundTrip(t *testing.T) {
|
||||
t.Errorf("Marshal %T: got %#q, want %#q", obj, got, wantEncoded)
|
||||
}
|
||||
|
||||
var cmp testType
|
||||
var cmp testPtrType
|
||||
if err := jsontypes.Unmarshal(bits, &cmp); err != nil {
|
||||
t.Errorf("Unmarshal %#q failed: %v", string(bits), err)
|
||||
}
|
||||
@@ -63,8 +74,10 @@ func TestRoundTrip(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unregistered", func(t *testing.T) {
|
||||
obj := testType{Field: "hello"}
|
||||
t.Run("RoundTrip_bareType", func(t *testing.T) {
|
||||
const wantEncoded = `{"type":"test/BareType","value":{"field":"hello"}}`
|
||||
|
||||
obj := testBareType{Field: "hello"}
|
||||
bits, err := jsontypes.Marshal(&obj)
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal %T failed: %v", obj, err)
|
||||
@@ -73,11 +86,103 @@ func TestRoundTrip(t *testing.T) {
|
||||
t.Errorf("Marshal %T: got %#q, want %#q", obj, got, wantEncoded)
|
||||
}
|
||||
|
||||
var cmp struct {
|
||||
Field string `json:"field"`
|
||||
}
|
||||
var cmp testBareType
|
||||
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("Unmarshal_nilPointer", func(t *testing.T) {
|
||||
var obj *testBareType
|
||||
|
||||
// Unmarshaling to a nil pointer target should report an error.
|
||||
if err := jsontypes.Unmarshal([]byte(`null`), obj); err == nil {
|
||||
t.Errorf("Unmarshal nil: got %+v, wanted error", obj)
|
||||
} else {
|
||||
t.Logf("Unmarshal correctly failed: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal_bareType", func(t *testing.T) {
|
||||
const want = "foobar"
|
||||
const input = `{"type":"test/BareType","value":{"field":"` + want + `"}}`
|
||||
|
||||
var obj testBareType
|
||||
if err := jsontypes.Unmarshal([]byte(input), &obj); err != nil {
|
||||
t.Fatalf("Unmarshal failed: %v", err)
|
||||
}
|
||||
if obj.Field != want {
|
||||
t.Errorf("Unmarshal result: got %q, want %q", obj.Field, want)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal_bareType_interface", func(t *testing.T) {
|
||||
const want = "foobar"
|
||||
const input = `{"type":"test/BareType","value":{"field":"` + want + `"}}`
|
||||
|
||||
var obj fielder
|
||||
if err := jsontypes.Unmarshal([]byte(input), &obj); err != nil {
|
||||
t.Fatalf("Unmarshal failed: %v", err)
|
||||
}
|
||||
if got := obj.Value(); got != want {
|
||||
t.Errorf("Unmarshal result: got %q, want %q", got, want)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal_pointerType", func(t *testing.T) {
|
||||
const want = "bazquux"
|
||||
const input = `{"type":"test/PointerType","value":{"field":"` + want + `"}}`
|
||||
|
||||
var obj testPtrType
|
||||
if err := jsontypes.Unmarshal([]byte(input), &obj); err != nil {
|
||||
t.Fatalf("Unmarshal failed: %v", err)
|
||||
}
|
||||
if obj.Field != want {
|
||||
t.Errorf("Unmarshal result: got %q, want %q", obj.Field, want)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal_pointerType_interface", func(t *testing.T) {
|
||||
const want = "foobar"
|
||||
const input = `{"type":"test/PointerType","value":{"field":"` + want + `"}}`
|
||||
|
||||
var obj fielder
|
||||
if err := jsontypes.Unmarshal([]byte(input), &obj); err != nil {
|
||||
t.Fatalf("Unmarshal failed: %v", err)
|
||||
}
|
||||
if got := obj.Value(); got != want {
|
||||
t.Errorf("Unmarshal result: got %q, want %q", got, want)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal_unknownTypeTag", func(t *testing.T) {
|
||||
const input = `{"type":"test/Nonesuch","value":null}`
|
||||
|
||||
// An unregistered type tag in a valid envelope should report an error.
|
||||
var obj interface{}
|
||||
if err := jsontypes.Unmarshal([]byte(input), &obj); err == nil {
|
||||
t.Errorf("Unmarshal: got %+v, wanted error", obj)
|
||||
} else {
|
||||
t.Logf("Unmarshal correctly failed: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unmarshal_similarTarget", func(t *testing.T) {
|
||||
const want = "zootie-zoot-zoot"
|
||||
const input = `{"type":"test/PointerType","value":{"field":"` + want + `"}}`
|
||||
|
||||
// The target has a compatible (i.e., assignable) shape to the registered
|
||||
// type. This should work even though it's not the original named type.
|
||||
var cmp struct {
|
||||
Field string `json:"field"`
|
||||
}
|
||||
if err := jsontypes.Unmarshal([]byte(input), &cmp); err != nil {
|
||||
t.Errorf("Unmarshal %#q failed: %v", input, err)
|
||||
} else if cmp.Field != want {
|
||||
t.Errorf("Unmarshal result: got %q, want %q", cmp.Field, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package mempool
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -214,7 +215,7 @@ func TestReactorBroadcastTxs(t *testing.T) {
|
||||
|
||||
// regression test for https://github.com/tendermint/tendermint/issues/5408
|
||||
func TestReactorConcurrency(t *testing.T) {
|
||||
numTxs := 5
|
||||
numTxs := 10
|
||||
numNodes := 2
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
@@ -229,7 +230,7 @@ func TestReactorConcurrency(t *testing.T) {
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
for i := 0; i < runtime.NumCPU()*2; i++ {
|
||||
wg.Add(2)
|
||||
|
||||
// 1. submit a bunch of txs
|
||||
@@ -266,9 +267,6 @@ func TestReactorConcurrency(t *testing.T) {
|
||||
err := mempool.Update(ctx, 1, []types.Tx{}, make([]*abci.ResponseDeliverTx, 0), nil, nil)
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
|
||||
// flush the mempool
|
||||
rts.mempools[secondary].Flush()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
@@ -3,6 +3,7 @@ package core
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -21,7 +22,6 @@ import (
|
||||
sm "github.com/tendermint/tendermint/internal/state"
|
||||
"github.com/tendermint/tendermint/internal/state/indexer"
|
||||
"github.com/tendermint/tendermint/internal/statesync"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/libs/strings"
|
||||
"github.com/tendermint/tendermint/rpc/coretypes"
|
||||
@@ -154,7 +154,7 @@ func (env *Environment) InitGenesisChunks() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
data, err := tmjson.Marshal(env.GenDoc)
|
||||
data, err := json.Marshal(env.GenDoc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -5,25 +5,22 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/tendermint/tendermint/rpc/coretypes"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// BroadcastEvidence broadcasts evidence of the misbehavior.
|
||||
// More: https://docs.tendermint.com/master/rpc/#/Evidence/broadcast_evidence
|
||||
func (env *Environment) BroadcastEvidence(
|
||||
ctx context.Context,
|
||||
ev types.Evidence) (*coretypes.ResultBroadcastEvidence, error) {
|
||||
|
||||
if ev == nil {
|
||||
ev coretypes.Evidence,
|
||||
) (*coretypes.ResultBroadcastEvidence, error) {
|
||||
if ev.Value == nil {
|
||||
return nil, fmt.Errorf("%w: no evidence was provided", coretypes.ErrInvalidRequest)
|
||||
}
|
||||
|
||||
if err := ev.ValidateBasic(); err != nil {
|
||||
if err := ev.Value.ValidateBasic(); err != nil {
|
||||
return nil, fmt.Errorf("evidence.ValidateBasic failed: %w", err)
|
||||
}
|
||||
|
||||
if err := env.EvidencePool.AddEvidence(ev); err != nil {
|
||||
if err := env.EvidencePool.AddEvidence(ev.Value); err != nil {
|
||||
return nil, fmt.Errorf("failed to add evidence: %w", err)
|
||||
}
|
||||
return &coretypes.ResultBroadcastEvidence{Hash: ev.Hash()}, nil
|
||||
return &coretypes.ResultBroadcastEvidence{Hash: ev.Value.Hash()}, nil
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ type RPCService interface {
|
||||
BlockResults(ctx context.Context, heightPtr *int64) (*coretypes.ResultBlockResults, error)
|
||||
BlockSearch(ctx context.Context, query string, pagePtr, perPagePtr *int, orderBy string) (*coretypes.ResultBlockSearch, error)
|
||||
BlockchainInfo(ctx context.Context, minHeight, maxHeight int64) (*coretypes.ResultBlockchainInfo, error)
|
||||
BroadcastEvidence(ctx context.Context, ev types.Evidence) (*coretypes.ResultBroadcastEvidence, error)
|
||||
BroadcastEvidence(ctx context.Context, ev coretypes.Evidence) (*coretypes.ResultBroadcastEvidence, error)
|
||||
BroadcastTxAsync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error)
|
||||
BroadcastTxCommit(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTxCommit, error)
|
||||
BroadcastTxSync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error)
|
||||
|
||||
@@ -1,278 +0,0 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Unmarshal unmarshals JSON into the given value, using Amino-compatible JSON encoding (strings
|
||||
// for 64-bit numbers, and type wrappers for registered types).
|
||||
func Unmarshal(bz []byte, v interface{}) error {
|
||||
return decode(bz, v)
|
||||
}
|
||||
|
||||
func decode(bz []byte, v interface{}) error {
|
||||
if len(bz) == 0 {
|
||||
return errors.New("cannot decode empty bytes")
|
||||
}
|
||||
|
||||
rv := reflect.ValueOf(v)
|
||||
if rv.Kind() != reflect.Ptr {
|
||||
return errors.New("must decode into a pointer")
|
||||
}
|
||||
rv = rv.Elem()
|
||||
|
||||
// If this is a registered type, defer to interface decoder regardless of whether the input is
|
||||
// an interface or a bare value. This retains Amino's behavior, but is inconsistent with
|
||||
// behavior in structs where an interface field will get the type wrapper while a bare value
|
||||
// field will not.
|
||||
if typeRegistry.name(rv.Type()) != "" {
|
||||
return decodeReflectInterface(bz, rv)
|
||||
}
|
||||
|
||||
return decodeReflect(bz, rv)
|
||||
}
|
||||
|
||||
func decodeReflect(bz []byte, rv reflect.Value) error {
|
||||
if !rv.CanAddr() {
|
||||
return errors.New("value is not addressable")
|
||||
}
|
||||
|
||||
// Handle null for slices, interfaces, and pointers
|
||||
if bytes.Equal(bz, []byte("null")) {
|
||||
rv.Set(reflect.Zero(rv.Type()))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Dereference-and-construct pointers, to handle nested pointers.
|
||||
for rv.Kind() == reflect.Ptr {
|
||||
if rv.IsNil() {
|
||||
rv.Set(reflect.New(rv.Type().Elem()))
|
||||
}
|
||||
rv = rv.Elem()
|
||||
}
|
||||
|
||||
// Times must be UTC and end with Z
|
||||
if rv.Type() == timeType {
|
||||
switch {
|
||||
case len(bz) < 2 || bz[0] != '"' || bz[len(bz)-1] != '"':
|
||||
return fmt.Errorf("JSON time must be an RFC3339 string, but got %q", bz)
|
||||
case bz[len(bz)-2] != 'Z':
|
||||
return fmt.Errorf("JSON time must be UTC and end with 'Z', but got %q", bz)
|
||||
}
|
||||
}
|
||||
|
||||
// If value implements json.Umarshaler, call it.
|
||||
if rv.Addr().Type().Implements(jsonUnmarshalerType) {
|
||||
return rv.Addr().Interface().(json.Unmarshaler).UnmarshalJSON(bz)
|
||||
}
|
||||
|
||||
switch rv.Type().Kind() {
|
||||
// Decode complex types recursively.
|
||||
case reflect.Slice, reflect.Array:
|
||||
return decodeReflectList(bz, rv)
|
||||
|
||||
case reflect.Map:
|
||||
return decodeReflectMap(bz, rv)
|
||||
|
||||
case reflect.Struct:
|
||||
return decodeReflectStruct(bz, rv)
|
||||
|
||||
case reflect.Interface:
|
||||
return decodeReflectInterface(bz, rv)
|
||||
|
||||
// For 64-bit integers, unwrap expected string and defer to stdlib for integer decoding.
|
||||
case reflect.Int64, reflect.Int, reflect.Uint64, reflect.Uint:
|
||||
if bz[0] != '"' || bz[len(bz)-1] != '"' {
|
||||
return fmt.Errorf("invalid 64-bit integer encoding %q, expected string", string(bz))
|
||||
}
|
||||
bz = bz[1 : len(bz)-1]
|
||||
fallthrough
|
||||
|
||||
// Anything else we defer to the stdlib.
|
||||
default:
|
||||
return decodeStdlib(bz, rv)
|
||||
}
|
||||
}
|
||||
|
||||
func decodeReflectList(bz []byte, rv reflect.Value) error {
|
||||
if !rv.CanAddr() {
|
||||
return errors.New("list value is not addressable")
|
||||
}
|
||||
|
||||
switch rv.Type().Elem().Kind() {
|
||||
// Decode base64-encoded bytes using stdlib decoder, via byte slice for arrays.
|
||||
case reflect.Uint8:
|
||||
if rv.Type().Kind() == reflect.Array {
|
||||
var buf []byte
|
||||
if err := json.Unmarshal(bz, &buf); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(buf) != rv.Len() {
|
||||
return fmt.Errorf("got %v bytes, expected %v", len(buf), rv.Len())
|
||||
}
|
||||
reflect.Copy(rv, reflect.ValueOf(buf))
|
||||
|
||||
} else if err := decodeStdlib(bz, rv); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Decode anything else into a raw JSON slice, and decode values recursively.
|
||||
default:
|
||||
var rawSlice []json.RawMessage
|
||||
if err := json.Unmarshal(bz, &rawSlice); err != nil {
|
||||
return err
|
||||
}
|
||||
if rv.Type().Kind() == reflect.Slice {
|
||||
rv.Set(reflect.MakeSlice(reflect.SliceOf(rv.Type().Elem()), len(rawSlice), len(rawSlice)))
|
||||
}
|
||||
if rv.Len() != len(rawSlice) { // arrays of wrong size
|
||||
return fmt.Errorf("got list of %v elements, expected %v", len(rawSlice), rv.Len())
|
||||
}
|
||||
for i, bz := range rawSlice {
|
||||
if err := decodeReflect(bz, rv.Index(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Replace empty slices with nil slices, for Amino compatibility
|
||||
if rv.Type().Kind() == reflect.Slice && rv.Len() == 0 {
|
||||
rv.Set(reflect.Zero(rv.Type()))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeReflectMap(bz []byte, rv reflect.Value) error {
|
||||
if !rv.CanAddr() {
|
||||
return errors.New("map value is not addressable")
|
||||
}
|
||||
|
||||
// Decode into a raw JSON map, using string keys.
|
||||
rawMap := make(map[string]json.RawMessage)
|
||||
if err := json.Unmarshal(bz, &rawMap); err != nil {
|
||||
return err
|
||||
}
|
||||
if rv.Type().Key().Kind() != reflect.String {
|
||||
return fmt.Errorf("map keys must be strings, got %v", rv.Type().Key().String())
|
||||
}
|
||||
|
||||
// Recursively decode values.
|
||||
rv.Set(reflect.MakeMapWithSize(rv.Type(), len(rawMap)))
|
||||
for key, bz := range rawMap {
|
||||
value := reflect.New(rv.Type().Elem()).Elem()
|
||||
if err := decodeReflect(bz, value); err != nil {
|
||||
return err
|
||||
}
|
||||
rv.SetMapIndex(reflect.ValueOf(key), value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeReflectStruct(bz []byte, rv reflect.Value) error {
|
||||
if !rv.CanAddr() {
|
||||
return errors.New("struct value is not addressable")
|
||||
}
|
||||
sInfo := makeStructInfo(rv.Type())
|
||||
|
||||
// Decode raw JSON values into a string-keyed map.
|
||||
rawMap := make(map[string]json.RawMessage)
|
||||
if err := json.Unmarshal(bz, &rawMap); err != nil {
|
||||
return err
|
||||
}
|
||||
for i, fInfo := range sInfo.fields {
|
||||
if !fInfo.hidden {
|
||||
frv := rv.Field(i)
|
||||
bz := rawMap[fInfo.jsonName]
|
||||
if len(bz) > 0 {
|
||||
if err := decodeReflect(bz, frv); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if !fInfo.omitEmpty {
|
||||
frv.Set(reflect.Zero(frv.Type()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeReflectInterface(bz []byte, rv reflect.Value) error {
|
||||
if !rv.CanAddr() {
|
||||
return errors.New("interface value not addressable")
|
||||
}
|
||||
|
||||
// Decode the interface wrapper.
|
||||
wrapper := interfaceWrapper{}
|
||||
if err := json.Unmarshal(bz, &wrapper); err != nil {
|
||||
return err
|
||||
}
|
||||
if wrapper.Type == "" {
|
||||
return errors.New("interface type cannot be empty")
|
||||
}
|
||||
if len(wrapper.Value) == 0 {
|
||||
return errors.New("interface value cannot be empty")
|
||||
}
|
||||
|
||||
// Dereference-and-construct pointers, to handle nested pointers.
|
||||
for rv.Kind() == reflect.Ptr {
|
||||
if rv.IsNil() {
|
||||
rv.Set(reflect.New(rv.Type().Elem()))
|
||||
}
|
||||
rv = rv.Elem()
|
||||
}
|
||||
|
||||
// Look up the interface type, and construct a concrete value.
|
||||
rt, returnPtr := typeRegistry.lookup(wrapper.Type)
|
||||
if rt == nil {
|
||||
return fmt.Errorf("unknown type %q", wrapper.Type)
|
||||
}
|
||||
|
||||
cptr := reflect.New(rt)
|
||||
crv := cptr.Elem()
|
||||
if err := decodeReflect(wrapper.Value, crv); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// This makes sure interface implementations with pointer receivers (e.g. func (c *Car)) are
|
||||
// constructed as pointers behind the interface. The types must be registered as pointers with
|
||||
// RegisterType().
|
||||
if rv.Type().Kind() == reflect.Interface && returnPtr {
|
||||
if !cptr.Type().AssignableTo(rv.Type()) {
|
||||
return fmt.Errorf("invalid type %q for this value", wrapper.Type)
|
||||
}
|
||||
rv.Set(cptr)
|
||||
} else {
|
||||
if !crv.Type().AssignableTo(rv.Type()) {
|
||||
return fmt.Errorf("invalid type %q for this value", wrapper.Type)
|
||||
}
|
||||
rv.Set(crv)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeStdlib(bz []byte, rv reflect.Value) error {
|
||||
if !rv.CanAddr() && rv.Kind() != reflect.Ptr {
|
||||
return errors.New("value must be addressable or pointer")
|
||||
}
|
||||
|
||||
// Make sure we are unmarshaling into a pointer.
|
||||
target := rv
|
||||
if rv.Kind() != reflect.Ptr {
|
||||
target = reflect.New(rv.Type())
|
||||
}
|
||||
if err := json.Unmarshal(bz, target.Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
rv.Set(target.Elem())
|
||||
return nil
|
||||
}
|
||||
|
||||
type interfaceWrapper struct {
|
||||
Type string `json:"type"`
|
||||
Value json.RawMessage `json:"value"`
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
package json_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/json"
|
||||
)
|
||||
|
||||
func TestUnmarshal(t *testing.T) {
|
||||
i64Nil := (*int64)(nil)
|
||||
str := "string"
|
||||
strPtr := &str
|
||||
structNil := (*Struct)(nil)
|
||||
i32 := int32(32)
|
||||
i64 := int64(64)
|
||||
|
||||
testcases := map[string]struct {
|
||||
json string
|
||||
value interface{}
|
||||
err bool
|
||||
}{
|
||||
"bool true": {"true", true, false},
|
||||
"bool false": {"false", false, false},
|
||||
"float32": {"3.14", float32(3.14), false},
|
||||
"float64": {"3.14", float64(3.14), false},
|
||||
"int32": {`32`, int32(32), false},
|
||||
"int32 string": {`"32"`, int32(32), true},
|
||||
"int32 ptr": {`32`, &i32, false},
|
||||
"int64": {`"64"`, int64(64), false},
|
||||
"int64 noend": {`"64`, int64(64), true},
|
||||
"int64 number": {`64`, int64(64), true},
|
||||
"int64 ptr": {`"64"`, &i64, false},
|
||||
"int64 ptr nil": {`null`, i64Nil, false},
|
||||
"string": {`"foo"`, "foo", false},
|
||||
"string noend": {`"foo`, "foo", true},
|
||||
"string ptr": {`"string"`, &str, false},
|
||||
"slice byte": {`"AQID"`, []byte{1, 2, 3}, false},
|
||||
"slice bytes": {`["AQID"]`, [][]byte{{1, 2, 3}}, false},
|
||||
"slice int32": {`[1,2,3]`, []int32{1, 2, 3}, false},
|
||||
"slice int64": {`["1","2","3"]`, []int64{1, 2, 3}, false},
|
||||
"slice int64 number": {`[1,2,3]`, []int64{1, 2, 3}, true},
|
||||
"slice int64 ptr": {`["64"]`, []*int64{&i64}, false},
|
||||
"slice int64 empty": {`[]`, []int64(nil), false},
|
||||
"slice int64 null": {`null`, []int64(nil), false},
|
||||
"array byte": {`"AQID"`, [3]byte{1, 2, 3}, false},
|
||||
"array byte large": {`"AQID"`, [4]byte{1, 2, 3, 4}, true},
|
||||
"array byte small": {`"AQID"`, [2]byte{1, 2}, true},
|
||||
"array int32": {`[1,2,3]`, [3]int32{1, 2, 3}, false},
|
||||
"array int64": {`["1","2","3"]`, [3]int64{1, 2, 3}, false},
|
||||
"array int64 number": {`[1,2,3]`, [3]int64{1, 2, 3}, true},
|
||||
"array int64 large": {`["1","2","3"]`, [4]int64{1, 2, 3, 4}, true},
|
||||
"array int64 small": {`["1","2","3"]`, [2]int64{1, 2}, true},
|
||||
"map bytes": {`{"b":"AQID"}`, map[string][]byte{"b": {1, 2, 3}}, false},
|
||||
"map int32": {`{"a":1,"b":2}`, map[string]int32{"a": 1, "b": 2}, false},
|
||||
"map int64": {`{"a":"1","b":"2"}`, map[string]int64{"a": 1, "b": 2}, false},
|
||||
"map int64 empty": {`{}`, map[string]int64{}, false},
|
||||
"map int64 null": {`null`, map[string]int64(nil), false},
|
||||
"map int key": {`{}`, map[int]int{}, true},
|
||||
"time": {`"2020-06-03T17:35:30Z"`, time.Date(2020, 6, 3, 17, 35, 30, 0, time.UTC), false},
|
||||
"time non-utc": {`"2020-06-03T17:35:30+02:00"`, time.Time{}, true},
|
||||
"time nozone": {`"2020-06-03T17:35:30"`, time.Time{}, true},
|
||||
"car": {`{"type":"vehicle/car","value":{"Wheels":4}}`, Car{Wheels: 4}, false},
|
||||
"car ptr": {`{"type":"vehicle/car","value":{"Wheels":4}}`, &Car{Wheels: 4}, false},
|
||||
"car iface": {`{"type":"vehicle/car","value":{"Wheels":4}}`, Vehicle(&Car{Wheels: 4}), false},
|
||||
"boat": {`{"type":"vehicle/boat","value":{"Sail":true}}`, Boat{Sail: true}, false},
|
||||
"boat ptr": {`{"type":"vehicle/boat","value":{"Sail":true}}`, &Boat{Sail: true}, false},
|
||||
"boat iface": {`{"type":"vehicle/boat","value":{"Sail":true}}`, Vehicle(Boat{Sail: true}), false},
|
||||
"boat into car": {`{"type":"vehicle/boat","value":{"Sail":true}}`, Car{}, true},
|
||||
"boat into car iface": {`{"type":"vehicle/boat","value":{"Sail":true}}`, Vehicle(&Car{}), true},
|
||||
"shoes": {`{"type":"vehicle/shoes","value":{"Soles":"rubber"}}`, Car{}, true},
|
||||
"shoes ptr": {`{"type":"vehicle/shoes","value":{"Soles":"rubber"}}`, &Car{}, true},
|
||||
"shoes iface": {`{"type":"vehicle/shoes","value":{"Soles":"rubbes"}}`, Vehicle(&Car{}), true},
|
||||
"key public": {`{"type":"key/public","value":"AQIDBAUGBwg="}`, PublicKey{1, 2, 3, 4, 5, 6, 7, 8}, false},
|
||||
"key wrong": {`{"type":"key/public","value":"AQIDBAUGBwg="}`, PrivateKey{1, 2, 3, 4, 5, 6, 7, 8}, true},
|
||||
"key into car": {`{"type":"key/public","value":"AQIDBAUGBwg="}`, Vehicle(&Car{}), true},
|
||||
"tags": {
|
||||
`{"name":"name","OmitEmpty":"foo","Hidden":"bar","tags":{"name":"child"}}`,
|
||||
Tags{JSONName: "name", OmitEmpty: "foo", Tags: &Tags{JSONName: "child"}},
|
||||
false,
|
||||
},
|
||||
"tags ptr": {
|
||||
`{"name":"name","OmitEmpty":"foo","tags":null}`,
|
||||
&Tags{JSONName: "name", OmitEmpty: "foo"},
|
||||
false,
|
||||
},
|
||||
"tags real name": {`{"JSONName":"name"}`, Tags{}, false},
|
||||
"struct": {
|
||||
`{
|
||||
"Bool":true, "Float64":3.14, "Int32":32, "Int64":"64", "Int64Ptr":"64",
|
||||
"String":"foo", "StringPtrPtr": "string", "Bytes":"AQID",
|
||||
"Time":"2020-06-02T16:05:13.004346374Z",
|
||||
"Car":{"Wheels":4},
|
||||
"Boat":{"Sail":true},
|
||||
"Vehicles":[
|
||||
{"type":"vehicle/car","value":{"Wheels":4}},
|
||||
{"type":"vehicle/boat","value":{"Sail":true}}
|
||||
],
|
||||
"Child":{
|
||||
"Bool":false, "Float64":0, "Int32":0, "Int64":"0", "Int64Ptr":null,
|
||||
"String":"child", "StringPtrPtr":null, "Bytes":null,
|
||||
"Time":"0001-01-01T00:00:00Z",
|
||||
"Car":null, "Boat":{"Sail":false}, "Vehicles":null, "Child":null
|
||||
},
|
||||
"private": "foo", "unknown": "bar"
|
||||
}`,
|
||||
Struct{
|
||||
Bool: true, Float64: 3.14, Int32: 32, Int64: 64, Int64Ptr: &i64,
|
||||
String: "foo", StringPtrPtr: &strPtr, Bytes: []byte{1, 2, 3},
|
||||
Time: time.Date(2020, 6, 2, 16, 5, 13, 4346374, time.UTC),
|
||||
Car: &Car{Wheels: 4}, Boat: Boat{Sail: true}, Vehicles: []Vehicle{
|
||||
Vehicle(&Car{Wheels: 4}),
|
||||
Vehicle(Boat{Sail: true}),
|
||||
},
|
||||
Child: &Struct{Bool: false, String: "child"},
|
||||
},
|
||||
false,
|
||||
},
|
||||
"struct key into vehicle": {`{"Vehicles":[
|
||||
{"type":"vehicle/car","value":{"Wheels":4}},
|
||||
{"type":"key/public","value":"MTIzNDU2Nzg="}
|
||||
]}`, Struct{}, true},
|
||||
"struct ptr null": {`null`, structNil, false},
|
||||
"custom value": {`{"Value":"foo"}`, CustomValue{}, false},
|
||||
"custom ptr": {`"foo"`, &CustomPtr{Value: "custom"}, false},
|
||||
"custom ptr value": {`"foo"`, CustomPtr{Value: "custom"}, false},
|
||||
"invalid type": {`"foo"`, Struct{}, true},
|
||||
}
|
||||
for name, tc := range testcases {
|
||||
tc := tc
|
||||
t.Run(name, func(t *testing.T) {
|
||||
// Create a target variable as a pointer to the zero value of the tc.value type,
|
||||
// and wrap it in an empty interface. Decode into that interface.
|
||||
target := reflect.New(reflect.TypeOf(tc.value)).Interface()
|
||||
err := json.Unmarshal([]byte(tc.json), target)
|
||||
if tc.err {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
// Unwrap the target pointer and get the value behind the interface.
|
||||
actual := reflect.ValueOf(target).Elem().Interface()
|
||||
assert.Equal(t, tc.value, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
// Package json provides functions for marshaling and unmarshaling JSON in a format that is
|
||||
// backwards-compatible with Amino JSON encoding. This mostly differs from encoding/json in
|
||||
// encoding of integers (64-bit integers are encoded as strings, not numbers), and handling
|
||||
// of interfaces (wrapped in an interface object with type/value keys).
|
||||
//
|
||||
// JSON tags (e.g. `json:"name,omitempty"`) are supported in the same way as encoding/json, as is
|
||||
// custom marshaling overrides via the json.Marshaler and json.Unmarshaler interfaces.
|
||||
//
|
||||
// Note that not all JSON emitted by Tendermint is generated by this library; some is generated by
|
||||
// encoding/json instead, and kept like that for backwards compatibility.
|
||||
//
|
||||
// Encoding of numbers uses strings for 64-bit integers (including unspecified ints), to improve
|
||||
// compatibility with e.g. Javascript (which uses 64-bit floats for numbers, having 53-bit
|
||||
// precision):
|
||||
//
|
||||
// int32(32) // Output: 32
|
||||
// uint32(32) // Output: 32
|
||||
// int64(64) // Output: "64"
|
||||
// uint64(64) // Output: "64"
|
||||
// int(64) // Output: "64"
|
||||
// uint(64) // Output: "64"
|
||||
//
|
||||
// Encoding of other scalars follows encoding/json:
|
||||
//
|
||||
// nil // Output: null
|
||||
// true // Output: true
|
||||
// "foo" // Output: "foo"
|
||||
// "" // Output: ""
|
||||
//
|
||||
// Slices and arrays are encoded as encoding/json, including base64-encoding of byte slices
|
||||
// with additional base64-encoding of byte arrays as well:
|
||||
//
|
||||
// []int64(nil) // Output: null
|
||||
// []int64{} // Output: []
|
||||
// []int64{1, 2, 3} // Output: ["1", "2", "3"]
|
||||
// []int32{1, 2, 3} // Output: [1, 2, 3]
|
||||
// []byte{1, 2, 3} // Output: "AQID"
|
||||
// [3]int64{1, 2, 3} // Output: ["1", "2", "3"]
|
||||
// [3]byte{1, 2, 3} // Output: "AQID"
|
||||
//
|
||||
// Maps are encoded as encoding/json, but only strings are allowed as map keys (nil maps are not
|
||||
// emitted as null, to retain Amino backwards-compatibility):
|
||||
//
|
||||
// map[string]int64(nil) // Output: {}
|
||||
// map[string]int64{} // Output: {}
|
||||
// map[string]int64{"a":1,"b":2} // Output: {"a":"1","b":"2"}
|
||||
// map[string]int32{"a":1,"b":2} // Output: {"a":1,"b":2}
|
||||
// map[bool]int{true:1} // Errors
|
||||
//
|
||||
// Times are encoded as encoding/json, in RFC3339Nano format, but requiring UTC time zone (with zero
|
||||
// times emitted as "0001-01-01T00:00:00Z" as with encoding/json):
|
||||
//
|
||||
// time.Date(2020, 6, 8, 16, 21, 28, 123, time.FixedZone("UTC+2", 2*60*60))
|
||||
// // Output: "2020-06-08T14:21:28.000000123Z"
|
||||
// time.Time{} // Output: "0001-01-01T00:00:00Z"
|
||||
// (*time.Time)(nil) // Output: null
|
||||
//
|
||||
// Structs are encoded as encoding/json, supporting JSON tags and ignoring private fields:
|
||||
//
|
||||
// type Struct struct{
|
||||
// Name string
|
||||
// Value int32 `json:"value,omitempty"`
|
||||
// private bool
|
||||
// }
|
||||
//
|
||||
// Struct{Name: "foo", Value: 7, private: true} // Output: {"Name":"foo","value":7}
|
||||
// Struct{} // Output: {"Name":""}
|
||||
//
|
||||
// Registered types are encoded with type wrapper, regardless of whether they are given as interface
|
||||
// or bare struct, but inside structs they are only emitted with type wrapper for interface fields
|
||||
// (this follows Amino behavior):
|
||||
//
|
||||
// type Vehicle interface {
|
||||
// Drive() error
|
||||
// }
|
||||
//
|
||||
// type Car struct {
|
||||
// Wheels int8
|
||||
// }
|
||||
//
|
||||
// func (c *Car) Drive() error { return nil }
|
||||
//
|
||||
// RegisterType(&Car{}, "vehicle/car")
|
||||
//
|
||||
// Car{Wheels: 4} // Output: {"type":"vehicle/car","value":{"Wheels":4}}
|
||||
// &Car{Wheels: 4} // Output: {"type":"vehicle/car","value":{"Wheels":4}}
|
||||
// (*Car)(nil) // Output: null
|
||||
// Vehicle(Car{Wheels: 4}) // Output: {"type":"vehicle/car","value":{"Wheels":4}}
|
||||
// Vehicle(nil) // Output: null
|
||||
//
|
||||
// type Struct struct {
|
||||
// Car *Car
|
||||
// Vehicle Vehicle
|
||||
// }
|
||||
//
|
||||
// Struct{Car: &Car{Wheels: 4}, Vehicle: &Car{Wheels: 4}}
|
||||
// // Output: {"Car": {"Wheels: 4"}, "Vehicle": {"type":"vehicle/car","value":{"Wheels":4}}}
|
||||
//
|
||||
package json
|
||||
@@ -1,254 +0,0 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
timeType = reflect.TypeOf(time.Time{})
|
||||
jsonMarshalerType = reflect.TypeOf(new(json.Marshaler)).Elem()
|
||||
jsonUnmarshalerType = reflect.TypeOf(new(json.Unmarshaler)).Elem()
|
||||
)
|
||||
|
||||
// Marshal marshals the value as JSON, using Amino-compatible JSON encoding (strings for
|
||||
// 64-bit numbers, and type wrappers for registered types).
|
||||
func Marshal(v interface{}) ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
err := encode(buf, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// MarshalIndent marshals the value as JSON, using the given prefix and indentation.
|
||||
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
|
||||
bz, err := Marshal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
err = json.Indent(buf, bz, prefix, indent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func encode(w io.Writer, v interface{}) error {
|
||||
// Bare nil values can't be reflected, so we must handle them here.
|
||||
if v == nil {
|
||||
return writeStr(w, "null")
|
||||
}
|
||||
rv := reflect.ValueOf(v)
|
||||
|
||||
// If this is a registered type, defer to interface encoder regardless of whether the input is
|
||||
// an interface or a bare value. This retains Amino's behavior, but is inconsistent with
|
||||
// behavior in structs where an interface field will get the type wrapper while a bare value
|
||||
// field will not.
|
||||
if typeRegistry.name(rv.Type()) != "" {
|
||||
return encodeReflectInterface(w, rv)
|
||||
}
|
||||
|
||||
return encodeReflect(w, rv)
|
||||
}
|
||||
|
||||
func encodeReflect(w io.Writer, rv reflect.Value) error {
|
||||
if !rv.IsValid() {
|
||||
return errors.New("invalid reflect value")
|
||||
}
|
||||
|
||||
// Recursively dereference if pointer.
|
||||
for rv.Kind() == reflect.Ptr {
|
||||
if rv.IsNil() {
|
||||
return writeStr(w, "null")
|
||||
}
|
||||
rv = rv.Elem()
|
||||
}
|
||||
|
||||
// Convert times to UTC.
|
||||
if rv.Type() == timeType {
|
||||
rv = reflect.ValueOf(rv.Interface().(time.Time).Round(0).UTC())
|
||||
}
|
||||
|
||||
// If the value implements json.Marshaler, defer to stdlib directly. Since we've already
|
||||
// dereferenced, we try implementations with both value receiver and pointer receiver. We must
|
||||
// do this after the time normalization above, and thus after dereferencing.
|
||||
if rv.Type().Implements(jsonMarshalerType) {
|
||||
return encodeStdlib(w, rv.Interface())
|
||||
} else if rv.CanAddr() && rv.Addr().Type().Implements(jsonMarshalerType) {
|
||||
return encodeStdlib(w, rv.Addr().Interface())
|
||||
}
|
||||
|
||||
switch rv.Type().Kind() {
|
||||
// Complex types must be recursively encoded.
|
||||
case reflect.Interface:
|
||||
return encodeReflectInterface(w, rv)
|
||||
|
||||
case reflect.Array, reflect.Slice:
|
||||
return encodeReflectList(w, rv)
|
||||
|
||||
case reflect.Map:
|
||||
return encodeReflectMap(w, rv)
|
||||
|
||||
case reflect.Struct:
|
||||
return encodeReflectStruct(w, rv)
|
||||
|
||||
// 64-bit integers are emitted as strings, to avoid precision problems with e.g.
|
||||
// Javascript which uses 64-bit floats (having 53-bit precision).
|
||||
case reflect.Int64, reflect.Int:
|
||||
return writeStr(w, `"`+strconv.FormatInt(rv.Int(), 10)+`"`)
|
||||
|
||||
case reflect.Uint64, reflect.Uint:
|
||||
return writeStr(w, `"`+strconv.FormatUint(rv.Uint(), 10)+`"`)
|
||||
|
||||
// For everything else, defer to the stdlib encoding/json encoder
|
||||
default:
|
||||
return encodeStdlib(w, rv.Interface())
|
||||
}
|
||||
}
|
||||
|
||||
func encodeReflectList(w io.Writer, rv reflect.Value) error {
|
||||
// Emit nil slices as null.
|
||||
if rv.Kind() == reflect.Slice && rv.IsNil() {
|
||||
return writeStr(w, "null")
|
||||
}
|
||||
|
||||
// Encode byte slices as base64 with the stdlib encoder.
|
||||
if rv.Type().Elem().Kind() == reflect.Uint8 {
|
||||
// Stdlib does not base64-encode byte arrays, only slices, so we copy to slice.
|
||||
if rv.Type().Kind() == reflect.Array {
|
||||
slice := reflect.MakeSlice(reflect.SliceOf(rv.Type().Elem()), rv.Len(), rv.Len())
|
||||
reflect.Copy(slice, rv)
|
||||
rv = slice
|
||||
}
|
||||
return encodeStdlib(w, rv.Interface())
|
||||
}
|
||||
|
||||
// Anything else we recursively encode ourselves.
|
||||
length := rv.Len()
|
||||
if err := writeStr(w, "["); err != nil {
|
||||
return err
|
||||
}
|
||||
for i := 0; i < length; i++ {
|
||||
if err := encodeReflect(w, rv.Index(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
if i < length-1 {
|
||||
if err := writeStr(w, ","); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return writeStr(w, "]")
|
||||
}
|
||||
|
||||
func encodeReflectMap(w io.Writer, rv reflect.Value) error {
|
||||
if rv.Type().Key().Kind() != reflect.String {
|
||||
return errors.New("map key must be string")
|
||||
}
|
||||
|
||||
// nil maps are not emitted as nil, to retain Amino compatibility.
|
||||
|
||||
if err := writeStr(w, "{"); err != nil {
|
||||
return err
|
||||
}
|
||||
writeComma := false
|
||||
for _, keyrv := range rv.MapKeys() {
|
||||
if writeComma {
|
||||
if err := writeStr(w, ","); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := encodeStdlib(w, keyrv.Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeStr(w, ":"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := encodeReflect(w, rv.MapIndex(keyrv)); err != nil {
|
||||
return err
|
||||
}
|
||||
writeComma = true
|
||||
}
|
||||
return writeStr(w, "}")
|
||||
}
|
||||
|
||||
func encodeReflectStruct(w io.Writer, rv reflect.Value) error {
|
||||
sInfo := makeStructInfo(rv.Type())
|
||||
if err := writeStr(w, "{"); err != nil {
|
||||
return err
|
||||
}
|
||||
writeComma := false
|
||||
for i, fInfo := range sInfo.fields {
|
||||
frv := rv.Field(i)
|
||||
if fInfo.hidden || (fInfo.omitEmpty && frv.IsZero()) {
|
||||
continue
|
||||
}
|
||||
|
||||
if writeComma {
|
||||
if err := writeStr(w, ","); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := encodeStdlib(w, fInfo.jsonName); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeStr(w, ":"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := encodeReflect(w, frv); err != nil {
|
||||
return err
|
||||
}
|
||||
writeComma = true
|
||||
}
|
||||
return writeStr(w, "}")
|
||||
}
|
||||
|
||||
func encodeReflectInterface(w io.Writer, rv reflect.Value) error {
|
||||
// Get concrete value and dereference pointers.
|
||||
for rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface {
|
||||
if rv.IsNil() {
|
||||
return writeStr(w, "null")
|
||||
}
|
||||
rv = rv.Elem()
|
||||
}
|
||||
|
||||
// Look up the name of the concrete type
|
||||
name := typeRegistry.name(rv.Type())
|
||||
if name == "" {
|
||||
return fmt.Errorf("cannot encode unregistered type %v", rv.Type())
|
||||
}
|
||||
|
||||
// Write value wrapped in interface envelope
|
||||
if err := writeStr(w, fmt.Sprintf(`{"type":%q,"value":`, name)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := encodeReflect(w, rv); err != nil {
|
||||
return err
|
||||
}
|
||||
return writeStr(w, "}")
|
||||
}
|
||||
|
||||
func encodeStdlib(w io.Writer, v interface{}) error {
|
||||
// Doesn't stream the output because that adds a newline, as per:
|
||||
// https://golang.org/pkg/encoding/json/#Encoder.Encode
|
||||
blob, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(blob)
|
||||
return err
|
||||
}
|
||||
|
||||
func writeStr(w io.Writer, s string) error {
|
||||
_, err := w.Write([]byte(s))
|
||||
return err
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package json_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/json"
|
||||
)
|
||||
|
||||
func TestMarshal(t *testing.T) {
|
||||
s := "string"
|
||||
sPtr := &s
|
||||
i64 := int64(64)
|
||||
ti := time.Date(2020, 6, 2, 18, 5, 13, 4346374, time.FixedZone("UTC+2", 2*60*60))
|
||||
car := &Car{Wheels: 4}
|
||||
boat := Boat{Sail: true}
|
||||
|
||||
testcases := map[string]struct {
|
||||
value interface{}
|
||||
output string
|
||||
}{
|
||||
"nil": {nil, `null`},
|
||||
"string": {"foo", `"foo"`},
|
||||
"float32": {float32(3.14), `3.14`},
|
||||
"float32 neg": {float32(-3.14), `-3.14`},
|
||||
"float64": {float64(3.14), `3.14`},
|
||||
"float64 neg": {float64(-3.14), `-3.14`},
|
||||
"int32": {int32(32), `32`},
|
||||
"int64": {int64(64), `"64"`},
|
||||
"int64 neg": {int64(-64), `"-64"`},
|
||||
"int64 ptr": {&i64, `"64"`},
|
||||
"uint64": {uint64(64), `"64"`},
|
||||
"time": {ti, `"2020-06-02T16:05:13.004346374Z"`},
|
||||
"time empty": {time.Time{}, `"0001-01-01T00:00:00Z"`},
|
||||
"time ptr": {&ti, `"2020-06-02T16:05:13.004346374Z"`},
|
||||
"customptr": {CustomPtr{Value: "x"}, `{"Value":"x"}`}, // same as encoding/json
|
||||
"customptr ptr": {&CustomPtr{Value: "x"}, `"custom"`},
|
||||
"customvalue": {CustomValue{Value: "x"}, `"custom"`},
|
||||
"customvalue ptr": {&CustomValue{Value: "x"}, `"custom"`},
|
||||
"slice nil": {[]int(nil), `null`},
|
||||
"slice empty": {[]int{}, `[]`},
|
||||
"slice bytes": {[]byte{1, 2, 3}, `"AQID"`},
|
||||
"slice int64": {[]int64{1, 2, 3}, `["1","2","3"]`},
|
||||
"slice int64 ptr": {[]*int64{&i64, nil}, `["64",null]`},
|
||||
"array bytes": {[3]byte{1, 2, 3}, `"AQID"`},
|
||||
"array int64": {[3]int64{1, 2, 3}, `["1","2","3"]`},
|
||||
"map nil": {map[string]int64(nil), `{}`}, // retain Amino compatibility
|
||||
"map empty": {map[string]int64{}, `{}`},
|
||||
"map int64": {map[string]int64{"a": 1, "b": 2, "c": 3}, `{"a":"1","b":"2","c":"3"}`},
|
||||
"car": {car, `{"type":"vehicle/car","value":{"Wheels":4}}`},
|
||||
"car value": {*car, `{"type":"vehicle/car","value":{"Wheels":4}}`},
|
||||
"car iface": {Vehicle(car), `{"type":"vehicle/car","value":{"Wheels":4}}`},
|
||||
"car nil": {(*Car)(nil), `null`},
|
||||
"boat": {boat, `{"type":"vehicle/boat","value":{"Sail":true}}`},
|
||||
"boat ptr": {&boat, `{"type":"vehicle/boat","value":{"Sail":true}}`},
|
||||
"boat iface": {Vehicle(boat), `{"type":"vehicle/boat","value":{"Sail":true}}`},
|
||||
"key public": {PublicKey{1, 2, 3, 4, 5, 6, 7, 8}, `{"type":"key/public","value":"AQIDBAUGBwg="}`},
|
||||
"tags": {
|
||||
Tags{JSONName: "name", OmitEmpty: "foo", Hidden: "bar", Tags: &Tags{JSONName: "child"}},
|
||||
`{"name":"name","OmitEmpty":"foo","tags":{"name":"child"}}`,
|
||||
},
|
||||
"tags empty": {Tags{}, `{"name":""}`},
|
||||
// The encoding of the Car and Boat fields do not have type wrappers, even though they get
|
||||
// type wrappers when encoded directly (see "car" and "boat" tests). This is to retain the
|
||||
// same behavior as Amino. If the field was a Vehicle interface instead, it would get
|
||||
// type wrappers, as seen in the Vehicles field.
|
||||
"struct": {
|
||||
Struct{
|
||||
Bool: true, Float64: 3.14, Int32: 32, Int64: 64, Int64Ptr: &i64,
|
||||
String: "foo", StringPtrPtr: &sPtr, Bytes: []byte{1, 2, 3},
|
||||
Time: ti, Car: car, Boat: boat, Vehicles: []Vehicle{car, boat},
|
||||
Child: &Struct{Bool: false, String: "child"}, private: "private",
|
||||
},
|
||||
`{
|
||||
"Bool":true, "Float64":3.14, "Int32":32, "Int64":"64", "Int64Ptr":"64",
|
||||
"String":"foo", "StringPtrPtr": "string", "Bytes":"AQID",
|
||||
"Time":"2020-06-02T16:05:13.004346374Z",
|
||||
"Car":{"Wheels":4},
|
||||
"Boat":{"Sail":true},
|
||||
"Vehicles":[
|
||||
{"type":"vehicle/car","value":{"Wheels":4}},
|
||||
{"type":"vehicle/boat","value":{"Sail":true}}
|
||||
],
|
||||
"Child":{
|
||||
"Bool":false, "Float64":0, "Int32":0, "Int64":"0", "Int64Ptr":null,
|
||||
"String":"child", "StringPtrPtr":null, "Bytes":null,
|
||||
"Time":"0001-01-01T00:00:00Z",
|
||||
"Car":null, "Boat":{"Sail":false}, "Vehicles":null, "Child":null
|
||||
}
|
||||
}`,
|
||||
},
|
||||
}
|
||||
for name, tc := range testcases {
|
||||
tc := tc
|
||||
t.Run(name, func(t *testing.T) {
|
||||
bz, err := json.Marshal(tc.value)
|
||||
require.NoError(t, err)
|
||||
assert.JSONEq(t, tc.output, string(bz))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package json_test
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/json"
|
||||
)
|
||||
|
||||
// Register Car, an instance of the Vehicle interface.
|
||||
func init() {
|
||||
json.RegisterType(&Car{}, "vehicle/car")
|
||||
json.RegisterType(Boat{}, "vehicle/boat")
|
||||
json.RegisterType(PublicKey{}, "key/public")
|
||||
json.RegisterType(PrivateKey{}, "key/private")
|
||||
}
|
||||
|
||||
type Vehicle interface {
|
||||
Drive() error
|
||||
}
|
||||
|
||||
// Car is a pointer implementation of Vehicle.
|
||||
type Car struct {
|
||||
Wheels int32
|
||||
}
|
||||
|
||||
func (c *Car) Drive() error { return nil }
|
||||
|
||||
// Boat is a value implementation of Vehicle.
|
||||
type Boat struct {
|
||||
Sail bool
|
||||
}
|
||||
|
||||
func (b Boat) Drive() error { return nil }
|
||||
|
||||
// These are public and private encryption keys.
|
||||
type PublicKey [8]byte
|
||||
type PrivateKey [8]byte
|
||||
|
||||
// Custom has custom marshalers and unmarshalers, taking pointer receivers.
|
||||
type CustomPtr struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
func (c *CustomPtr) MarshalJSON() ([]byte, error) {
|
||||
return []byte("\"custom\""), nil
|
||||
}
|
||||
|
||||
func (c *CustomPtr) UnmarshalJSON(bz []byte) error {
|
||||
c.Value = "custom"
|
||||
return nil
|
||||
}
|
||||
|
||||
// CustomValue has custom marshalers and unmarshalers, taking value receivers (which usually doesn't
|
||||
// make much sense since the unmarshaler can't change anything).
|
||||
type CustomValue struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
func (c CustomValue) MarshalJSON() ([]byte, error) {
|
||||
return []byte("\"custom\""), nil
|
||||
}
|
||||
|
||||
func (c CustomValue) UnmarshalJSON(bz []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Tags tests JSON tags.
|
||||
type Tags struct {
|
||||
JSONName string `json:"name"`
|
||||
OmitEmpty string `json:",omitempty"`
|
||||
Hidden string `json:"-"`
|
||||
Tags *Tags `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
// Struct tests structs with lots of contents.
|
||||
type Struct struct {
|
||||
Bool bool
|
||||
Float64 float64
|
||||
Int32 int32
|
||||
Int64 int64
|
||||
Int64Ptr *int64
|
||||
String string
|
||||
StringPtrPtr **string
|
||||
Bytes []byte
|
||||
Time time.Time
|
||||
Car *Car
|
||||
Boat Boat
|
||||
Vehicles []Vehicle
|
||||
Child *Struct
|
||||
private string
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var (
|
||||
// cache caches struct info.
|
||||
cache = newStructInfoCache()
|
||||
)
|
||||
|
||||
// structCache is a cache of struct info.
|
||||
type structInfoCache struct {
|
||||
sync.RWMutex
|
||||
structInfos map[reflect.Type]*structInfo
|
||||
}
|
||||
|
||||
func newStructInfoCache() *structInfoCache {
|
||||
return &structInfoCache{
|
||||
structInfos: make(map[reflect.Type]*structInfo),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *structInfoCache) get(rt reflect.Type) *structInfo {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
return c.structInfos[rt]
|
||||
}
|
||||
|
||||
func (c *structInfoCache) set(rt reflect.Type, sInfo *structInfo) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
c.structInfos[rt] = sInfo
|
||||
}
|
||||
|
||||
// structInfo contains JSON info for a struct.
|
||||
type structInfo struct {
|
||||
fields []*fieldInfo
|
||||
}
|
||||
|
||||
// fieldInfo contains JSON info for a struct field.
|
||||
type fieldInfo struct {
|
||||
jsonName string
|
||||
omitEmpty bool
|
||||
hidden bool
|
||||
}
|
||||
|
||||
// makeStructInfo generates structInfo for a struct as a reflect.Value.
|
||||
func makeStructInfo(rt reflect.Type) *structInfo {
|
||||
if rt.Kind() != reflect.Struct {
|
||||
panic(fmt.Sprintf("can't make struct info for non-struct value %v", rt))
|
||||
}
|
||||
if sInfo := cache.get(rt); sInfo != nil {
|
||||
return sInfo
|
||||
}
|
||||
fields := make([]*fieldInfo, 0, rt.NumField())
|
||||
for i := 0; i < cap(fields); i++ {
|
||||
frt := rt.Field(i)
|
||||
fInfo := &fieldInfo{
|
||||
jsonName: frt.Name,
|
||||
omitEmpty: false,
|
||||
hidden: frt.Name == "" || !unicode.IsUpper(rune(frt.Name[0])),
|
||||
}
|
||||
o := frt.Tag.Get("json")
|
||||
if o == "-" {
|
||||
fInfo.hidden = true
|
||||
} else if o != "" {
|
||||
opts := strings.Split(o, ",")
|
||||
if opts[0] != "" {
|
||||
fInfo.jsonName = opts[0]
|
||||
}
|
||||
for _, o := range opts[1:] {
|
||||
if o == "omitempty" {
|
||||
fInfo.omitEmpty = true
|
||||
}
|
||||
}
|
||||
}
|
||||
fields = append(fields, fInfo)
|
||||
}
|
||||
sInfo := &structInfo{fields: fields}
|
||||
cache.set(rt, sInfo)
|
||||
return sInfo
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
// typeRegistry contains globally registered types for JSON encoding/decoding.
|
||||
typeRegistry = newTypes()
|
||||
)
|
||||
|
||||
// RegisterType registers a type for Amino-compatible interface encoding in the global type
|
||||
// registry. These types will be encoded with a type wrapper `{"type":"<type>","value":<value>}`
|
||||
// regardless of which interface they are wrapped in (if any). If the type is a pointer, it will
|
||||
// still be valid both for value and pointer types, but decoding into an interface will generate
|
||||
// the a value or pointer based on the registered type.
|
||||
//
|
||||
// Should only be called in init() functions, as it panics on error.
|
||||
func RegisterType(_type interface{}, name string) {
|
||||
if _type == nil {
|
||||
panic("cannot register nil type")
|
||||
}
|
||||
err := typeRegistry.register(name, reflect.ValueOf(_type).Type())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// typeInfo contains type information.
|
||||
type typeInfo struct {
|
||||
name string
|
||||
rt reflect.Type
|
||||
returnPtr bool
|
||||
}
|
||||
|
||||
// types is a type registry. It is safe for concurrent use.
|
||||
type types struct {
|
||||
sync.RWMutex
|
||||
byType map[reflect.Type]*typeInfo
|
||||
byName map[string]*typeInfo
|
||||
}
|
||||
|
||||
// newTypes creates a new type registry.
|
||||
func newTypes() types {
|
||||
return types{
|
||||
byType: map[reflect.Type]*typeInfo{},
|
||||
byName: map[string]*typeInfo{},
|
||||
}
|
||||
}
|
||||
|
||||
// registers the given type with the given name. The name and type must not be registered already.
|
||||
func (t *types) register(name string, rt reflect.Type) error {
|
||||
if name == "" {
|
||||
return errors.New("name cannot be empty")
|
||||
}
|
||||
// If this is a pointer type, we recursively resolve until we get a bare type, but register that
|
||||
// we should return pointers.
|
||||
returnPtr := false
|
||||
for rt.Kind() == reflect.Ptr {
|
||||
returnPtr = true
|
||||
rt = rt.Elem()
|
||||
}
|
||||
tInfo := &typeInfo{
|
||||
name: name,
|
||||
rt: rt,
|
||||
returnPtr: returnPtr,
|
||||
}
|
||||
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
if _, ok := t.byName[tInfo.name]; ok {
|
||||
return fmt.Errorf("a type with name %q is already registered", name)
|
||||
}
|
||||
if _, ok := t.byType[tInfo.rt]; ok {
|
||||
return fmt.Errorf("the type %v is already registered", rt)
|
||||
}
|
||||
t.byName[name] = tInfo
|
||||
t.byType[rt] = tInfo
|
||||
return nil
|
||||
}
|
||||
|
||||
// lookup looks up a type from a name, or nil if not registered.
|
||||
func (t *types) lookup(name string) (reflect.Type, bool) {
|
||||
t.RLock()
|
||||
defer t.RUnlock()
|
||||
tInfo := t.byName[name]
|
||||
if tInfo == nil {
|
||||
return nil, false
|
||||
}
|
||||
return tInfo.rt, tInfo.returnPtr
|
||||
}
|
||||
|
||||
// name looks up the name of a type, or empty if not registered. Unwraps pointers as necessary.
|
||||
func (t *types) name(rt reflect.Type) string {
|
||||
for rt.Kind() == reflect.Ptr {
|
||||
rt = rt.Elem()
|
||||
}
|
||||
t.RLock()
|
||||
defer t.RUnlock()
|
||||
tInfo := t.byType[rt]
|
||||
if tInfo == nil {
|
||||
return ""
|
||||
}
|
||||
return tInfo.name
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package mbt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@@ -8,7 +9,6 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
"github.com/tendermint/tendermint/light"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
@@ -28,7 +28,7 @@ func TestVerify(t *testing.T) {
|
||||
}
|
||||
|
||||
var tc testCase
|
||||
err = tmjson.Unmarshal(jsonBlob, &tc)
|
||||
err = json.Unmarshal(jsonBlob, &tc)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -103,7 +103,7 @@ type testCase struct {
|
||||
type initialData struct {
|
||||
SignedHeader types.SignedHeader `json:"signed_header"`
|
||||
NextValidatorSet types.ValidatorSet `json:"next_validator_set"`
|
||||
TrustingPeriod uint64 `json:"trusting_period"`
|
||||
TrustingPeriod uint64 `json:"trusting_period,string"`
|
||||
Now time.Time `json:"now"`
|
||||
}
|
||||
|
||||
|
||||
@@ -38,3 +38,7 @@ func (p proxyService) Unsubscribe(ctx context.Context, query string) (*coretypes
|
||||
func (p proxyService) UnsubscribeAll(ctx context.Context) (*coretypes.ResultUnsubscribe, error) {
|
||||
return p.UnsubscribeAllWS(ctx)
|
||||
}
|
||||
|
||||
func (p proxyService) BroadcastEvidence(ctx context.Context, ev coretypes.Evidence) (*coretypes.ResultBroadcastEvidence, error) {
|
||||
return p.Client.BroadcastEvidence(ctx, ev.Value)
|
||||
}
|
||||
|
||||
@@ -149,12 +149,24 @@ func (c *Client) ABCIQuery(ctx context.Context, path string, data tmbytes.HexByt
|
||||
}
|
||||
|
||||
// ABCIQueryWithOptions returns an error if opts.Prove is false.
|
||||
// ABCIQueryWithOptions returns the result for the given height (opts.Height).
|
||||
// If no height is provided, the results of the block preceding the latest are returned.
|
||||
func (c *Client) ABCIQueryWithOptions(ctx context.Context, path string, data tmbytes.HexBytes,
|
||||
opts rpcclient.ABCIQueryOptions) (*coretypes.ResultABCIQuery, error) {
|
||||
|
||||
// always request the proof
|
||||
opts.Prove = true
|
||||
|
||||
// Can't return the latest block results because we won't be able to
|
||||
// prove them. Return the results for the previous block instead.
|
||||
if opts.Height == 0 {
|
||||
res, err := c.next.Status(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't get latest height: %w", err)
|
||||
}
|
||||
opts.Height = res.SyncInfo.LatestBlockHeight - 1
|
||||
}
|
||||
|
||||
res, err := c.next.ABCIQueryWithOptions(ctx, path, data, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
+30
-12
@@ -18,7 +18,6 @@ import (
|
||||
"github.com/tendermint/tendermint/internal/libs/protoio"
|
||||
"github.com/tendermint/tendermint/internal/libs/tempfile"
|
||||
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
tmos "github.com/tendermint/tendermint/libs/os"
|
||||
tmtime "github.com/tendermint/tendermint/libs/time"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
@@ -49,13 +48,19 @@ func voteToStep(vote *tmproto.Vote) (int8, error) {
|
||||
|
||||
// FilePVKey stores the immutable part of PrivValidator.
|
||||
type FilePVKey struct {
|
||||
Address types.Address `json:"address"`
|
||||
PubKey crypto.PubKey `json:"pub_key"`
|
||||
PrivKey crypto.PrivKey `json:"priv_key"`
|
||||
Address types.Address
|
||||
PubKey crypto.PubKey
|
||||
PrivKey crypto.PrivKey
|
||||
|
||||
filePath string
|
||||
}
|
||||
|
||||
type filePVKeyJSON struct {
|
||||
Address types.Address `json:"address"`
|
||||
PubKey json.RawMessage `json:"pub_key"`
|
||||
PrivKey json.RawMessage `json:"priv_key"`
|
||||
}
|
||||
|
||||
func (pvKey FilePVKey) MarshalJSON() ([]byte, error) {
|
||||
pubk, err := jsontypes.Marshal(pvKey.PubKey)
|
||||
if err != nil {
|
||||
@@ -65,11 +70,24 @@ func (pvKey FilePVKey) MarshalJSON() ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(struct {
|
||||
Address types.Address `json:"address"`
|
||||
PubKey json.RawMessage `json:"pub_key"`
|
||||
PrivKey json.RawMessage `json:"priv_key"`
|
||||
}{Address: pvKey.Address, PubKey: pubk, PrivKey: privk})
|
||||
return json.Marshal(filePVKeyJSON{
|
||||
Address: pvKey.Address, PubKey: pubk, PrivKey: privk,
|
||||
})
|
||||
}
|
||||
|
||||
func (pvKey *FilePVKey) UnmarshalJSON(data []byte) error {
|
||||
var key filePVKeyJSON
|
||||
if err := json.Unmarshal(data, &key); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := jsontypes.Unmarshal(key.PubKey, &pvKey.PubKey); err != nil {
|
||||
return fmt.Errorf("decoding PubKey: %w", err)
|
||||
}
|
||||
if err := jsontypes.Unmarshal(key.PrivKey, &pvKey.PrivKey); err != nil {
|
||||
return fmt.Errorf("decoding PrivKey: %w", err)
|
||||
}
|
||||
pvKey.Address = key.Address
|
||||
return nil
|
||||
}
|
||||
|
||||
// Save persists the FilePVKey to its filePath.
|
||||
@@ -79,11 +97,11 @@ func (pvKey FilePVKey) Save() error {
|
||||
return errors.New("cannot save PrivValidator key: filePath not set")
|
||||
}
|
||||
|
||||
jsonBytes, err := tmjson.MarshalIndent(pvKey, "", " ")
|
||||
data, err := json.MarshalIndent(pvKey, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tempfile.WriteFileAtomic(outFile, jsonBytes, 0600)
|
||||
return tempfile.WriteFileAtomic(outFile, data, 0600)
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
@@ -216,7 +234,7 @@ func loadFilePV(keyFilePath, stateFilePath string, loadState bool) (*FilePV, err
|
||||
return nil, err
|
||||
}
|
||||
pvKey := FilePVKey{}
|
||||
err = tmjson.Unmarshal(keyJSONBytes, &pvKey)
|
||||
err = json.Unmarshal(keyJSONBytes, &pvKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading PrivValidator key from %v: %w", keyFilePath, err)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
tmrand "github.com/tendermint/tendermint/libs/rand"
|
||||
tmtime "github.com/tendermint/tendermint/libs/time"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
@@ -143,7 +142,7 @@ func TestUnmarshalValidatorKey(t *testing.T) {
|
||||
}`, addr, pubB64, privB64)
|
||||
|
||||
val := FilePVKey{}
|
||||
err := tmjson.Unmarshal([]byte(serialized), &val)
|
||||
err := json.Unmarshal([]byte(serialized), &val)
|
||||
require.NoError(t, err)
|
||||
|
||||
// make sure the values match
|
||||
@@ -152,7 +151,7 @@ func TestUnmarshalValidatorKey(t *testing.T) {
|
||||
assert.EqualValues(t, privKey, val.PrivKey)
|
||||
|
||||
// export it and make sure it is the same
|
||||
out, err := tmjson.Marshal(val)
|
||||
out, err := json.Marshal(val)
|
||||
require.NoError(t, err)
|
||||
assert.JSONEq(t, serialized, string(out))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package crypto
|
||||
|
||||
// These functions export type tags for use with internal/jsontypes.
|
||||
|
||||
func (*PublicKey) TypeTag() string { return "tendermint.crypto.PublicKey" }
|
||||
func (*PublicKey_Ed25519) TypeTag() string { return "tendermint.crypto.PublicKey_Ed25519" }
|
||||
func (*PublicKey_Secp256K1) TypeTag() string { return "tendermint.crypto.PublicKey_Secp256K1" }
|
||||
@@ -512,7 +512,7 @@ func (c *baseRPCClient) BroadcastEvidence(
|
||||
) (*coretypes.ResultBroadcastEvidence, error) {
|
||||
result := new(coretypes.ResultBroadcastEvidence)
|
||||
if err := c.caller.Call(ctx, "broadcast_evidence", evidenceArgs{
|
||||
Evidence: ev,
|
||||
Evidence: coretypes.Evidence{Value: ev},
|
||||
}, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -4,11 +4,8 @@ package http
|
||||
// from the client to the server.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/tendermint/tendermint/internal/jsontypes"
|
||||
"github.com/tendermint/tendermint/libs/bytes"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
"github.com/tendermint/tendermint/rpc/coretypes"
|
||||
)
|
||||
|
||||
type abciQueryArgs struct {
|
||||
@@ -64,17 +61,5 @@ type validatorArgs struct {
|
||||
}
|
||||
|
||||
type evidenceArgs struct {
|
||||
Evidence types.Evidence
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler to encode the evidence using the
|
||||
// wrapped concrete type of the implementation.
|
||||
func (e evidenceArgs) MarshalJSON() ([]byte, error) {
|
||||
ev, err := jsontypes.Marshal(e.Evidence)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(struct {
|
||||
Evidence json.RawMessage `json:"evidence"`
|
||||
}{Evidence: ev})
|
||||
Evidence coretypes.Evidence `json:"evidence"`
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
@@ -9,7 +10,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/internal/pubsub"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||
"github.com/tendermint/tendermint/rpc/coretypes"
|
||||
jsonrpcclient "github.com/tendermint/tendermint/rpc/jsonrpc/client"
|
||||
@@ -239,7 +239,7 @@ func (w *wsEvents) eventListener(ctx context.Context) {
|
||||
}
|
||||
|
||||
result := new(coretypes.ResultEvent)
|
||||
err := tmjson.Unmarshal(resp.Result, result)
|
||||
err := json.Unmarshal(resp.Result, result)
|
||||
if err != nil {
|
||||
w.Logger.Error("failed to unmarshal response", "err", err)
|
||||
continue
|
||||
|
||||
@@ -198,7 +198,7 @@ func (c *Local) BlockSearch(
|
||||
}
|
||||
|
||||
func (c *Local) BroadcastEvidence(ctx context.Context, ev types.Evidence) (*coretypes.ResultBroadcastEvidence, error) {
|
||||
return c.env.BroadcastEvidence(ctx, ev)
|
||||
return c.env.BroadcastEvidence(ctx, coretypes.Evidence{Value: ev})
|
||||
}
|
||||
|
||||
func (c *Local) Subscribe(
|
||||
|
||||
@@ -155,5 +155,5 @@ func (c Client) Validators(ctx context.Context, height *int64, page, perPage *in
|
||||
}
|
||||
|
||||
func (c Client) BroadcastEvidence(ctx context.Context, ev types.Evidence) (*coretypes.ResultBroadcastEvidence, error) {
|
||||
return c.env.BroadcastEvidence(ctx, ev)
|
||||
return c.env.BroadcastEvidence(ctx, coretypes.Evidence{Value: ev})
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
@@ -21,7 +22,6 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto/encoding"
|
||||
"github.com/tendermint/tendermint/internal/mempool"
|
||||
rpccore "github.com/tendermint/tendermint/internal/rpc/core"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmmath "github.com/tendermint/tendermint/libs/math"
|
||||
"github.com/tendermint/tendermint/libs/service"
|
||||
@@ -305,7 +305,7 @@ func TestClientMethodCalls(t *testing.T) {
|
||||
doc := []byte(strings.Join(decoded, ""))
|
||||
|
||||
var out types.GenesisDoc
|
||||
require.NoError(t, tmjson.Unmarshal(doc, &out),
|
||||
require.NoError(t, json.Unmarshal(doc, &out),
|
||||
"first: %+v, doc: %s", first, string(doc))
|
||||
})
|
||||
t.Run("ABCIQuery", func(t *testing.T) {
|
||||
@@ -582,7 +582,7 @@ func TestClientMethodCalls(t *testing.T) {
|
||||
})
|
||||
t.Run("BroadcastEmpty", func(t *testing.T) {
|
||||
_, err := c.BroadcastEvidence(ctx, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+115
-39
@@ -3,10 +3,12 @@ package coretypes
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/internal/jsontypes"
|
||||
"github.com/tendermint/tendermint/libs/bytes"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
@@ -26,7 +28,7 @@ var (
|
||||
|
||||
// List of blocks
|
||||
type ResultBlockchainInfo struct {
|
||||
LastHeight int64 `json:"last_height"`
|
||||
LastHeight int64 `json:"last_height,string"`
|
||||
BlockMetas []*types.BlockMeta `json:"block_metas"`
|
||||
}
|
||||
|
||||
@@ -40,8 +42,8 @@ type ResultGenesis struct {
|
||||
// document to JSON and then splitting the resulting payload into
|
||||
// 16 megabyte blocks and then base64 encoding each block.
|
||||
type ResultGenesisChunk struct {
|
||||
ChunkNumber int `json:"chunk"`
|
||||
TotalChunks int `json:"total"`
|
||||
ChunkNumber int `json:"chunk,string"`
|
||||
TotalChunks int `json:"total,string"`
|
||||
Data string `json:"data"`
|
||||
}
|
||||
|
||||
@@ -64,9 +66,9 @@ type ResultCommit struct {
|
||||
|
||||
// ABCI results from a block
|
||||
type ResultBlockResults struct {
|
||||
Height int64 `json:"height"`
|
||||
Height int64 `json:"height,string"`
|
||||
TxsResults []*abci.ResponseDeliverTx `json:"txs_results"`
|
||||
TotalGasUsed int64 `json:"total_gas_used"`
|
||||
TotalGasUsed int64 `json:"total_gas_used,string"`
|
||||
BeginBlockEvents []abci.Event `json:"begin_block_events"`
|
||||
EndBlockEvents []abci.Event `json:"end_block_events"`
|
||||
ValidatorUpdates []abci.ValidatorUpdate `json:"validator_updates"`
|
||||
@@ -91,35 +93,64 @@ func NewResultCommit(header *types.Header, commit *types.Commit,
|
||||
type SyncInfo struct {
|
||||
LatestBlockHash bytes.HexBytes `json:"latest_block_hash"`
|
||||
LatestAppHash bytes.HexBytes `json:"latest_app_hash"`
|
||||
LatestBlockHeight int64 `json:"latest_block_height"`
|
||||
LatestBlockHeight int64 `json:"latest_block_height,string"`
|
||||
LatestBlockTime time.Time `json:"latest_block_time"`
|
||||
|
||||
EarliestBlockHash bytes.HexBytes `json:"earliest_block_hash"`
|
||||
EarliestAppHash bytes.HexBytes `json:"earliest_app_hash"`
|
||||
EarliestBlockHeight int64 `json:"earliest_block_height"`
|
||||
EarliestBlockHeight int64 `json:"earliest_block_height,string"`
|
||||
EarliestBlockTime time.Time `json:"earliest_block_time"`
|
||||
|
||||
MaxPeerBlockHeight int64 `json:"max_peer_block_height"`
|
||||
MaxPeerBlockHeight int64 `json:"max_peer_block_height,string"`
|
||||
|
||||
CatchingUp bool `json:"catching_up"`
|
||||
|
||||
TotalSyncedTime time.Duration `json:"total_synced_time"`
|
||||
RemainingTime time.Duration `json:"remaining_time"`
|
||||
TotalSyncedTime time.Duration `json:"total_synced_time,string"`
|
||||
RemainingTime time.Duration `json:"remaining_time,string"`
|
||||
|
||||
TotalSnapshots int64 `json:"total_snapshots"`
|
||||
ChunkProcessAvgTime time.Duration `json:"chunk_process_avg_time"`
|
||||
SnapshotHeight int64 `json:"snapshot_height"`
|
||||
SnapshotChunksCount int64 `json:"snapshot_chunks_count"`
|
||||
SnapshotChunksTotal int64 `json:"snapshot_chunks_total"`
|
||||
BackFilledBlocks int64 `json:"backfilled_blocks"`
|
||||
BackFillBlocksTotal int64 `json:"backfill_blocks_total"`
|
||||
TotalSnapshots int64 `json:"total_snapshots,string"`
|
||||
ChunkProcessAvgTime time.Duration `json:"chunk_process_avg_time,string"`
|
||||
SnapshotHeight int64 `json:"snapshot_height,string"`
|
||||
SnapshotChunksCount int64 `json:"snapshot_chunks_count,string"`
|
||||
SnapshotChunksTotal int64 `json:"snapshot_chunks_total,string"`
|
||||
BackFilledBlocks int64 `json:"backfilled_blocks,string"`
|
||||
BackFillBlocksTotal int64 `json:"backfill_blocks_total,string"`
|
||||
}
|
||||
|
||||
// Info about the node's validator
|
||||
type ValidatorInfo struct {
|
||||
Address bytes.HexBytes `json:"address"`
|
||||
PubKey crypto.PubKey `json:"pub_key"`
|
||||
VotingPower int64 `json:"voting_power"`
|
||||
Address bytes.HexBytes
|
||||
PubKey crypto.PubKey
|
||||
VotingPower int64
|
||||
}
|
||||
|
||||
type validatorInfoJSON struct {
|
||||
Address bytes.HexBytes `json:"address"`
|
||||
PubKey json.RawMessage `json:"pub_key"`
|
||||
VotingPower int64 `json:"voting_power,string"`
|
||||
}
|
||||
|
||||
func (v ValidatorInfo) MarshalJSON() ([]byte, error) {
|
||||
pk, err := jsontypes.Marshal(v.PubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(validatorInfoJSON{
|
||||
Address: v.Address, PubKey: pk, VotingPower: v.VotingPower,
|
||||
})
|
||||
}
|
||||
|
||||
func (v *ValidatorInfo) UnmarshalJSON(data []byte) error {
|
||||
var val validatorInfoJSON
|
||||
if err := json.Unmarshal(data, &val); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := jsontypes.Unmarshal(val.PubKey, &v.PubKey); err != nil {
|
||||
return err
|
||||
}
|
||||
v.Address = val.Address
|
||||
v.VotingPower = val.VotingPower
|
||||
return nil
|
||||
}
|
||||
|
||||
// Node Status
|
||||
@@ -142,7 +173,7 @@ func (s *ResultStatus) TxIndexEnabled() bool {
|
||||
type ResultNetInfo struct {
|
||||
Listening bool `json:"listening"`
|
||||
Listeners []string `json:"listeners"`
|
||||
NPeers int `json:"n_peers"`
|
||||
NPeers int `json:"n_peers,string"`
|
||||
Peers []Peer `json:"peers"`
|
||||
}
|
||||
|
||||
@@ -164,12 +195,11 @@ type Peer struct {
|
||||
|
||||
// Validators for a height.
|
||||
type ResultValidators struct {
|
||||
BlockHeight int64 `json:"block_height"`
|
||||
BlockHeight int64 `json:"block_height,string"`
|
||||
Validators []*types.Validator `json:"validators"`
|
||||
// Count of actual validators in this result
|
||||
Count int `json:"count"`
|
||||
// Total number of validators
|
||||
Total int `json:"total"`
|
||||
|
||||
Count int `json:"count,string"` // Count of actual validators in this result
|
||||
Total int `json:"total,string"` // Total number of validators
|
||||
}
|
||||
|
||||
// ConsensusParams for given height
|
||||
@@ -203,8 +233,7 @@ type ResultBroadcastTx struct {
|
||||
Log string `json:"log"`
|
||||
Codespace string `json:"codespace"`
|
||||
MempoolError string `json:"mempool_error"`
|
||||
|
||||
Hash bytes.HexBytes `json:"hash"`
|
||||
Hash bytes.HexBytes `json:"hash"`
|
||||
}
|
||||
|
||||
// CheckTx and DeliverTx results
|
||||
@@ -212,7 +241,7 @@ type ResultBroadcastTxCommit struct {
|
||||
CheckTx abci.ResponseCheckTx `json:"check_tx"`
|
||||
DeliverTx abci.ResponseDeliverTx `json:"deliver_tx"`
|
||||
Hash bytes.HexBytes `json:"hash"`
|
||||
Height int64 `json:"height"`
|
||||
Height int64 `json:"height,string"`
|
||||
}
|
||||
|
||||
// ResultCheckTx wraps abci.ResponseCheckTx.
|
||||
@@ -223,7 +252,7 @@ type ResultCheckTx struct {
|
||||
// Result of querying for a tx
|
||||
type ResultTx struct {
|
||||
Hash bytes.HexBytes `json:"hash"`
|
||||
Height int64 `json:"height"`
|
||||
Height int64 `json:"height,string"`
|
||||
Index uint32 `json:"index"`
|
||||
TxResult abci.ResponseDeliverTx `json:"tx_result"`
|
||||
Tx types.Tx `json:"tx"`
|
||||
@@ -233,20 +262,20 @@ type ResultTx struct {
|
||||
// Result of searching for txs
|
||||
type ResultTxSearch struct {
|
||||
Txs []*ResultTx `json:"txs"`
|
||||
TotalCount int `json:"total_count"`
|
||||
TotalCount int `json:"total_count,string"`
|
||||
}
|
||||
|
||||
// ResultBlockSearch defines the RPC response type for a block search by events.
|
||||
type ResultBlockSearch struct {
|
||||
Blocks []*ResultBlock `json:"blocks"`
|
||||
TotalCount int `json:"total_count"`
|
||||
TotalCount int `json:"total_count,string"`
|
||||
}
|
||||
|
||||
// List of mempool txs
|
||||
type ResultUnconfirmedTxs struct {
|
||||
Count int `json:"n_txs"`
|
||||
Total int `json:"total"`
|
||||
TotalBytes int64 `json:"total_bytes"`
|
||||
Count int `json:"n_txs,string"`
|
||||
Total int `json:"total,string"`
|
||||
TotalBytes int64 `json:"total_bytes,string"`
|
||||
Txs []types.Tx `json:"txs"`
|
||||
}
|
||||
|
||||
@@ -276,8 +305,55 @@ type (
|
||||
|
||||
// Event data from a subscription
|
||||
type ResultEvent struct {
|
||||
SubscriptionID string `json:"subscription_id"`
|
||||
Query string `json:"query"`
|
||||
Data types.TMEventData `json:"data"`
|
||||
Events []abci.Event `json:"events"`
|
||||
SubscriptionID string
|
||||
Query string
|
||||
Data types.TMEventData
|
||||
Events []abci.Event
|
||||
}
|
||||
|
||||
type resultEventJSON struct {
|
||||
SubscriptionID string `json:"subscription_id"`
|
||||
Query string `json:"query"`
|
||||
Data json.RawMessage `json:"data"`
|
||||
Events []abci.Event `json:"events"`
|
||||
}
|
||||
|
||||
func (r ResultEvent) MarshalJSON() ([]byte, error) {
|
||||
data, ok := r.Data.(jsontypes.Tagged)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("type %T is not tagged", r.Data)
|
||||
}
|
||||
evt, err := jsontypes.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(resultEventJSON{
|
||||
SubscriptionID: r.SubscriptionID,
|
||||
Query: r.Query,
|
||||
Data: evt,
|
||||
Events: r.Events,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *ResultEvent) UnmarshalJSON(data []byte) error {
|
||||
var res resultEventJSON
|
||||
if err := json.Unmarshal(data, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := jsontypes.Unmarshal(res.Data, &r.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
r.SubscriptionID = res.SubscriptionID
|
||||
r.Query = res.Query
|
||||
r.Events = res.Events
|
||||
return nil
|
||||
}
|
||||
|
||||
// Evidence is an argument wrapper for a types.Evidence value, that handles
|
||||
// encoding and decoding through JSON.
|
||||
type Evidence struct {
|
||||
Value types.Evidence
|
||||
}
|
||||
|
||||
func (e Evidence) MarshalJSON() ([]byte, error) { return jsontypes.Marshal(e.Value) }
|
||||
func (e *Evidence) UnmarshalJSON(data []byte) error { return jsontypes.Unmarshal(data, &e.Value) }
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
|
||||
)
|
||||
|
||||
@@ -26,7 +25,7 @@ func unmarshalResponseBytes(responseBytes []byte, expectedID rpctypes.JSONRPCInt
|
||||
}
|
||||
|
||||
// Unmarshal the RawMessage into the result.
|
||||
if err := tmjson.Unmarshal(response.Result, result); err != nil {
|
||||
if err := json.Unmarshal(response.Result, result); err != nil {
|
||||
return fmt.Errorf("error unmarshaling result: %w", err)
|
||||
}
|
||||
return nil
|
||||
@@ -71,7 +70,7 @@ func unmarshalResponseBytesArray(
|
||||
}
|
||||
|
||||
for i := 0; i < len(responses); i++ {
|
||||
if err := tmjson.Unmarshal(responses[i].Result, results[i]); err != nil {
|
||||
if err := json.Unmarshal(responses[i].Result, results[i]); err != nil {
|
||||
return nil, fmt.Errorf("error unmarshaling #%d result: %w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -9,8 +10,8 @@ import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/rpc/coretypes"
|
||||
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
|
||||
@@ -63,7 +64,12 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han
|
||||
continue
|
||||
}
|
||||
|
||||
args, err := parseParams(rpcFunc, hreq, req)
|
||||
req := req
|
||||
ctx := rpctypes.WithCallInfo(hreq.Context(), &rpctypes.CallInfo{
|
||||
RPCRequest: &req,
|
||||
HTTPRequest: hreq,
|
||||
})
|
||||
args, err := parseParams(ctx, rpcFunc, req.Params)
|
||||
if err != nil {
|
||||
responses = append(responses, rpctypes.RPCInvalidParamsError(
|
||||
req.ID, fmt.Errorf("converting JSON parameters: %w", err)))
|
||||
@@ -132,99 +138,101 @@ func parseRequests(data []byte) ([]rpctypes.RPCRequest, error) {
|
||||
return reqs, nil
|
||||
}
|
||||
|
||||
func mapParamsToArgs(
|
||||
rpcFunc *RPCFunc,
|
||||
params map[string]json.RawMessage,
|
||||
argsOffset int,
|
||||
) ([]reflect.Value, error) {
|
||||
|
||||
values := make([]reflect.Value, len(rpcFunc.argNames))
|
||||
for i, argName := range rpcFunc.argNames {
|
||||
argType := rpcFunc.args[i+argsOffset]
|
||||
|
||||
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
|
||||
}
|
||||
values[i] = val.Elem()
|
||||
} else { // use default for that type
|
||||
values[i] = reflect.Zero(argType)
|
||||
}
|
||||
}
|
||||
|
||||
return values, nil
|
||||
}
|
||||
|
||||
func arrayParamsToArgs(
|
||||
rpcFunc *RPCFunc,
|
||||
params []json.RawMessage,
|
||||
argsOffset int,
|
||||
) ([]reflect.Value, error) {
|
||||
|
||||
if len(rpcFunc.argNames) != len(params) {
|
||||
return nil, fmt.Errorf("expected %v parameters (%v), got %v (%v)",
|
||||
len(rpcFunc.argNames), rpcFunc.argNames, len(params), params)
|
||||
}
|
||||
|
||||
values := make([]reflect.Value, len(params))
|
||||
for i, p := range params {
|
||||
argType := rpcFunc.args[i+argsOffset]
|
||||
val := reflect.New(argType)
|
||||
err := tmjson.Unmarshal(p, val.Interface())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values[i] = val.Elem()
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
func parseParams(ctx context.Context, fn *RPCFunc, paramData []byte) ([]reflect.Value, error) {
|
||||
params, err := parseJSONParams(fn, paramData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return append(args, fargs...), nil
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
if isPtr {
|
||||
args[i+1] = pval
|
||||
} else {
|
||||
args[i+1] = pval.Elem()
|
||||
}
|
||||
}
|
||||
return args, nil
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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
|
||||
|
||||
// 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)
|
||||
} 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)
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// Otherwise, try an array.
|
||||
var a []json.RawMessage
|
||||
err = json.Unmarshal(raw, &a)
|
||||
if err == nil {
|
||||
return arrayParamsToArgs(rpcFunc, a, argsOffset)
|
||||
}
|
||||
return nil, errors.New("parameters must be an object or an array")
|
||||
}
|
||||
|
||||
// Otherwise, bad format, we cannot parse
|
||||
return nil, fmt.Errorf("unknown type for JSON params: %v. Expected map or array", err)
|
||||
// isStringValue reports whether data is a JSON string value.
|
||||
func isStringValue(data json.RawMessage) bool {
|
||||
return len(data) != 0 && data[0] == '"'
|
||||
}
|
||||
|
||||
type int64String int64
|
||||
|
||||
func (z *int64String) UnmarshalText(data []byte) error {
|
||||
v, err := strconv.ParseInt(string(data), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*z = int64String(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// writes a list of available rpc endpoints as an html page
|
||||
|
||||
@@ -46,7 +46,7 @@ func TestRPCParams(t *testing.T) {
|
||||
// id not captured in JSON parsing failures
|
||||
{`{"method": "c", "id": "0", "params": a}`, "invalid character", nil},
|
||||
{`{"method": "c", "id": "0", "params": ["a"]}`, "got 1", rpctypes.JSONRPCStringID("0")},
|
||||
{`{"method": "c", "id": "0", "params": ["a", "b"]}`, "invalid character", rpctypes.JSONRPCStringID("0")},
|
||||
{`{"method": "c", "id": "0", "params": ["a", "b"]}`, "invalid syntax", rpctypes.JSONRPCStringID("0")},
|
||||
{`{"method": "c", "id": "0", "params": [1, 1]}`, "of type string", rpctypes.JSONRPCStringID("0")},
|
||||
|
||||
// no ID - notification
|
||||
|
||||
@@ -153,17 +153,17 @@ func TestParseJSONRPC(t *testing.T) {
|
||||
{`[7,"flew",100]`, 0, "", true},
|
||||
{`{"name": -12, "height": "fred"}`, 0, "", true},
|
||||
}
|
||||
ctx := context.Background()
|
||||
for idx, tc := range cases {
|
||||
i := strconv.Itoa(idx)
|
||||
data := []byte(tc.raw)
|
||||
vals, err := jsonParamsToArgs(call, data)
|
||||
vals, err := parseParams(ctx, call, []byte(tc.raw))
|
||||
if tc.fail {
|
||||
assert.Error(t, err, i)
|
||||
} else {
|
||||
assert.NoError(t, err, "%s: %+v", i, err)
|
||||
if assert.Equal(t, 2, len(vals), i) {
|
||||
assert.Equal(t, tc.height, vals[0].Int(), i)
|
||||
assert.Equal(t, tc.name, vals[1].String(), i)
|
||||
if assert.Equal(t, 3, len(vals), i) { // ctx, height, name
|
||||
assert.Equal(t, tc.height, vals[1].Int(), i)
|
||||
assert.Equal(t, tc.name, vals[2].String(), i)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
@@ -371,18 +370,14 @@ func (wsc *wsConnection) readRoutine(ctx context.Context) {
|
||||
RPCRequest: &request,
|
||||
WSConn: wsc,
|
||||
})
|
||||
args := []reflect.Value{reflect.ValueOf(fctx)}
|
||||
if len(request.Params) > 0 {
|
||||
fnArgs, err := jsonParamsToArgs(rpcFunc, request.Params)
|
||||
if err != nil {
|
||||
if err := wsc.WriteRPCResponse(writeCtx,
|
||||
rpctypes.RPCInvalidParamsError(request.ID, fmt.Errorf("error converting json params to arguments: %w", err)),
|
||||
); err != nil {
|
||||
wsc.Logger.Error("error writing RPC response", "err", err)
|
||||
}
|
||||
continue
|
||||
args, err := parseParams(fctx, rpcFunc, request.Params)
|
||||
if err != nil {
|
||||
if err := wsc.WriteRPCResponse(writeCtx, rpctypes.RPCInvalidParamsError(
|
||||
request.ID, fmt.Errorf("error converting json params to arguments: %w", err)),
|
||||
); err != nil {
|
||||
wsc.Logger.Error("error writing RPC response", "err", err)
|
||||
}
|
||||
args = append(args, fnArgs...)
|
||||
continue
|
||||
}
|
||||
|
||||
returns := rpcFunc.f.Call(args)
|
||||
|
||||
@@ -7,8 +7,6 @@ import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
)
|
||||
|
||||
// a wrapper to emulate a sum type: jsonrpcid = string | int
|
||||
@@ -100,29 +98,10 @@ func (req RPCRequest) String() string {
|
||||
|
||||
// ParamsToRequest constructs a new RPCRequest with the given ID, method, and parameters.
|
||||
func ParamsToRequest(id jsonrpcid, method string, params interface{}) (RPCRequest, error) {
|
||||
var payload json.RawMessage
|
||||
var err error
|
||||
switch t := params.(type) {
|
||||
case map[string]interface{}:
|
||||
// TODO(creachadair): This special case preserves existing behavior that
|
||||
// relies on the custom JSON encoding library. Remove it once that
|
||||
// requirement has been removed.
|
||||
paramsMap := make(map[string]json.RawMessage, len(t))
|
||||
for name, value := range t {
|
||||
valueJSON, err := tmjson.Marshal(value)
|
||||
if err != nil {
|
||||
return RPCRequest{}, err
|
||||
}
|
||||
paramsMap[name] = valueJSON
|
||||
}
|
||||
payload, err = json.Marshal(paramsMap)
|
||||
default:
|
||||
payload, err = json.Marshal(params)
|
||||
}
|
||||
payload, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
return RPCRequest{}, err
|
||||
}
|
||||
|
||||
return NewRPCRequest(id, method, payload), nil
|
||||
}
|
||||
|
||||
@@ -162,6 +141,7 @@ func (resp *RPCResponse) UnmarshalJSON(data []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp.JSONRPC = unsafeResp.JSONRPC
|
||||
resp.Error = unsafeResp.Error
|
||||
resp.Result = unsafeResp.Result
|
||||
@@ -177,18 +157,11 @@ func (resp *RPCResponse) UnmarshalJSON(data []byte) error {
|
||||
}
|
||||
|
||||
func NewRPCSuccessResponse(id jsonrpcid, res interface{}) RPCResponse {
|
||||
var rawMsg json.RawMessage
|
||||
|
||||
if res != nil {
|
||||
var js []byte
|
||||
js, err := tmjson.Marshal(res)
|
||||
if err != nil {
|
||||
return RPCInternalError(id, fmt.Errorf("error marshaling response: %w", err))
|
||||
}
|
||||
rawMsg = json.RawMessage(js)
|
||||
result, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
return RPCInternalError(id, fmt.Errorf("error marshaling response: %w", err))
|
||||
}
|
||||
|
||||
return RPCResponse{JSONRPC: "2.0", ID: id, Result: rawMsg}
|
||||
return RPCResponse{JSONRPC: "2.0", ID: id, Result: result}
|
||||
}
|
||||
|
||||
func NewRPCErrorResponse(id jsonrpcid, code int, msg string, data string) RPCResponse {
|
||||
|
||||
@@ -9,13 +9,13 @@ package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/tendermint/tendermint/internal/consensus"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -56,7 +56,7 @@ func main() {
|
||||
}
|
||||
|
||||
var msg consensus.TimedWALMessage
|
||||
err = tmjson.Unmarshal(msgJSON, &msg)
|
||||
err = json.Unmarshal(msgJSON, &msg)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to unmarshal json: %w", err))
|
||||
}
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/tendermint/tendermint/internal/consensus"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -37,7 +37,7 @@ func main() {
|
||||
panic(fmt.Errorf("failed to decode msg: %w", err))
|
||||
}
|
||||
|
||||
json, err := tmjson.Marshal(msg)
|
||||
json, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to marshal msg: %w", err))
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
@@ -13,7 +14,6 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
"github.com/tendermint/tendermint/internal/test/factory"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
"github.com/tendermint/tendermint/privval"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
e2e "github.com/tendermint/tendermint/test/e2e/pkg"
|
||||
@@ -241,7 +241,7 @@ func readPrivKey(keyFilePath string) (crypto.PrivKey, error) {
|
||||
return nil, err
|
||||
}
|
||||
pvKey := privval.FilePVKey{}
|
||||
err = tmjson.Unmarshal(keyJSONBytes, &pvKey)
|
||||
err = json.Unmarshal(keyJSONBytes, &pvKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading PrivValidator key from %v: %w", keyFilePath, err)
|
||||
}
|
||||
|
||||
+50
-16
@@ -6,9 +6,9 @@ import (
|
||||
"strings"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/internal/jsontypes"
|
||||
tmpubsub "github.com/tendermint/tendermint/internal/pubsub"
|
||||
tmquery "github.com/tendermint/tendermint/internal/pubsub/query"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
)
|
||||
|
||||
// Reserved event types (alphabetically sorted).
|
||||
@@ -89,23 +89,21 @@ var (
|
||||
// ENCODING / DECODING
|
||||
|
||||
// TMEventData implements events.EventData.
|
||||
type TMEventData interface {
|
||||
// empty interface
|
||||
}
|
||||
type TMEventData interface{}
|
||||
|
||||
func init() {
|
||||
tmjson.RegisterType(EventDataNewBlock{}, "tendermint/event/NewBlock")
|
||||
tmjson.RegisterType(EventDataNewBlockHeader{}, "tendermint/event/NewBlockHeader")
|
||||
tmjson.RegisterType(EventDataNewEvidence{}, "tendermint/event/NewEvidence")
|
||||
tmjson.RegisterType(EventDataTx{}, "tendermint/event/Tx")
|
||||
tmjson.RegisterType(EventDataRoundState{}, "tendermint/event/RoundState")
|
||||
tmjson.RegisterType(EventDataNewRound{}, "tendermint/event/NewRound")
|
||||
tmjson.RegisterType(EventDataCompleteProposal{}, "tendermint/event/CompleteProposal")
|
||||
tmjson.RegisterType(EventDataVote{}, "tendermint/event/Vote")
|
||||
tmjson.RegisterType(EventDataValidatorSetUpdates{}, "tendermint/event/ValidatorSetUpdates")
|
||||
tmjson.RegisterType(EventDataString(""), "tendermint/event/ProposalString")
|
||||
tmjson.RegisterType(EventDataBlockSyncStatus{}, "tendermint/event/FastSyncStatus")
|
||||
tmjson.RegisterType(EventDataStateSyncStatus{}, "tendermint/event/StateSyncStatus")
|
||||
jsontypes.MustRegister(EventDataBlockSyncStatus{})
|
||||
jsontypes.MustRegister(EventDataCompleteProposal{})
|
||||
jsontypes.MustRegister(EventDataNewBlock{})
|
||||
jsontypes.MustRegister(EventDataNewBlockHeader{})
|
||||
jsontypes.MustRegister(EventDataNewEvidence{})
|
||||
jsontypes.MustRegister(EventDataNewRound{})
|
||||
jsontypes.MustRegister(EventDataRoundState{})
|
||||
jsontypes.MustRegister(EventDataStateSyncStatus{})
|
||||
jsontypes.MustRegister(EventDataTx{})
|
||||
jsontypes.MustRegister(EventDataValidatorSetUpdates{})
|
||||
jsontypes.MustRegister(EventDataVote{})
|
||||
jsontypes.MustRegister(EventDataString(""))
|
||||
}
|
||||
|
||||
// Most event messages are basic types (a block, a transaction)
|
||||
@@ -119,6 +117,9 @@ type EventDataNewBlock struct {
|
||||
ResultEndBlock abci.ResponseEndBlock `json:"result_end_block"`
|
||||
}
|
||||
|
||||
// TypeTag implements the required method of jsontypes.Tagged.
|
||||
func (EventDataNewBlock) TypeTag() string { return "tendermint/event/NewBlock" }
|
||||
|
||||
type EventDataNewBlockHeader struct {
|
||||
Header Header `json:"header"`
|
||||
|
||||
@@ -127,17 +128,26 @@ type EventDataNewBlockHeader struct {
|
||||
ResultEndBlock abci.ResponseEndBlock `json:"result_end_block"`
|
||||
}
|
||||
|
||||
// TypeTag implements the required method of jsontypes.Tagged.
|
||||
func (EventDataNewBlockHeader) TypeTag() string { return "tendermint/event/NewBlockHeader" }
|
||||
|
||||
type EventDataNewEvidence struct {
|
||||
Evidence Evidence `json:"evidence"`
|
||||
|
||||
Height int64 `json:"height"`
|
||||
}
|
||||
|
||||
// TypeTag implements the required method of jsontypes.Tagged.
|
||||
func (EventDataNewEvidence) TypeTag() string { return "tendermint/event/NewEvidence" }
|
||||
|
||||
// All txs fire EventDataTx
|
||||
type EventDataTx struct {
|
||||
abci.TxResult
|
||||
}
|
||||
|
||||
// TypeTag implements the required method of jsontypes.Tagged.
|
||||
func (EventDataTx) TypeTag() string { return "tendermint/event/Tx" }
|
||||
|
||||
// NOTE: This goes into the replay WAL
|
||||
type EventDataRoundState struct {
|
||||
Height int64 `json:"height"`
|
||||
@@ -145,6 +155,9 @@ type EventDataRoundState struct {
|
||||
Step string `json:"step"`
|
||||
}
|
||||
|
||||
// TypeTag implements the required method of jsontypes.Tagged.
|
||||
func (EventDataRoundState) TypeTag() string { return "tendermint/event/RoundState" }
|
||||
|
||||
type ValidatorInfo struct {
|
||||
Address Address `json:"address"`
|
||||
Index int32 `json:"index"`
|
||||
@@ -158,6 +171,9 @@ type EventDataNewRound struct {
|
||||
Proposer ValidatorInfo `json:"proposer"`
|
||||
}
|
||||
|
||||
// TypeTag implements the required method of jsontypes.Tagged.
|
||||
func (EventDataNewRound) TypeTag() string { return "tendermint/event/NewRound" }
|
||||
|
||||
type EventDataCompleteProposal struct {
|
||||
Height int64 `json:"height"`
|
||||
Round int32 `json:"round"`
|
||||
@@ -166,16 +182,28 @@ type EventDataCompleteProposal struct {
|
||||
BlockID BlockID `json:"block_id"`
|
||||
}
|
||||
|
||||
// TypeTag implements the required method of jsontypes.Tagged.
|
||||
func (EventDataCompleteProposal) TypeTag() string { return "tendermint/event/CompleteProposal" }
|
||||
|
||||
type EventDataVote struct {
|
||||
Vote *Vote
|
||||
}
|
||||
|
||||
// TypeTag implements the required method of jsontypes.Tagged.
|
||||
func (EventDataVote) TypeTag() string { return "tendermint/event/Vote" }
|
||||
|
||||
type EventDataString string
|
||||
|
||||
// TypeTag implements the required method of jsontypes.Tagged.
|
||||
func (EventDataString) TypeTag() string { return "tendermint/event/ProposalString" }
|
||||
|
||||
type EventDataValidatorSetUpdates struct {
|
||||
ValidatorUpdates []*Validator `json:"validator_updates"`
|
||||
}
|
||||
|
||||
// TypeTag implements the required method of jsontypes.Tagged.
|
||||
func (EventDataValidatorSetUpdates) TypeTag() string { return "tendermint/event/ValidatorSetUpdates" }
|
||||
|
||||
// EventDataBlockSyncStatus shows the fastsync status and the
|
||||
// height when the node state sync mechanism changes.
|
||||
type EventDataBlockSyncStatus struct {
|
||||
@@ -183,6 +211,9 @@ type EventDataBlockSyncStatus struct {
|
||||
Height int64 `json:"height"`
|
||||
}
|
||||
|
||||
// TypeTag implements the required method of jsontypes.Tagged.
|
||||
func (EventDataBlockSyncStatus) TypeTag() string { return "tendermint/event/FastSyncStatus" }
|
||||
|
||||
// EventDataStateSyncStatus shows the statesync status and the
|
||||
// height when the node state sync mechanism changes.
|
||||
type EventDataStateSyncStatus struct {
|
||||
@@ -190,6 +221,9 @@ type EventDataStateSyncStatus struct {
|
||||
Height int64 `json:"height"`
|
||||
}
|
||||
|
||||
// TypeTag implements the required method of jsontypes.Tagged.
|
||||
func (EventDataStateSyncStatus) TypeTag() string { return "tendermint/event/StateSyncStatus" }
|
||||
|
||||
// PUBSUB
|
||||
|
||||
const (
|
||||
|
||||
+28
-4
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
@@ -14,7 +15,6 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto/merkle"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
"github.com/tendermint/tendermint/internal/jsontypes"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
tmrand "github.com/tendermint/tendermint/libs/rand"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
@@ -554,6 +554,33 @@ func LightClientAttackEvidenceFromProto(lpb *tmproto.LightClientAttackEvidence)
|
||||
// EvidenceList is a list of Evidence. Evidences is not a word.
|
||||
type EvidenceList []Evidence
|
||||
|
||||
func (evl EvidenceList) MarshalJSON() ([]byte, error) {
|
||||
lst := make([]json.RawMessage, len(evl))
|
||||
for i, ev := range evl {
|
||||
bits, err := jsontypes.Marshal(ev)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lst[i] = bits
|
||||
}
|
||||
return json.Marshal(lst)
|
||||
}
|
||||
|
||||
func (evl *EvidenceList) UnmarshalJSON(data []byte) error {
|
||||
var lst []json.RawMessage
|
||||
if err := json.Unmarshal(data, &lst); err != nil {
|
||||
return err
|
||||
}
|
||||
out := make([]Evidence, len(lst))
|
||||
for i, elt := range lst {
|
||||
if err := jsontypes.Unmarshal(elt, &out[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
*evl = EvidenceList(out)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Hash returns the simple merkle root hash of the EvidenceList.
|
||||
func (evl EvidenceList) Hash() []byte {
|
||||
// These allocations are required because Evidence is not of type Bytes, and
|
||||
@@ -638,9 +665,6 @@ func EvidenceFromProto(evidence *tmproto.Evidence) (Evidence, error) {
|
||||
}
|
||||
|
||||
func init() {
|
||||
tmjson.RegisterType(&DuplicateVoteEvidence{}, "tendermint/DuplicateVoteEvidence")
|
||||
tmjson.RegisterType(&LightClientAttackEvidence{}, "tendermint/LightClientAttackEvidence")
|
||||
|
||||
jsontypes.MustRegister((*DuplicateVoteEvidence)(nil))
|
||||
jsontypes.MustRegister((*LightClientAttackEvidence)(nil))
|
||||
}
|
||||
|
||||
+31
-14
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/internal/jsontypes"
|
||||
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
tmtime "github.com/tendermint/tendermint/libs/time"
|
||||
)
|
||||
|
||||
@@ -28,10 +27,17 @@ const (
|
||||
|
||||
// GenesisValidator is an initial validator.
|
||||
type GenesisValidator struct {
|
||||
Address Address `json:"address"`
|
||||
PubKey crypto.PubKey `json:"pub_key"`
|
||||
Power int64 `json:"power,string"`
|
||||
Name string `json:"name"`
|
||||
Address Address
|
||||
PubKey crypto.PubKey
|
||||
Power int64
|
||||
Name string
|
||||
}
|
||||
|
||||
type genesisValidatorJSON struct {
|
||||
Address Address `json:"address"`
|
||||
PubKey json.RawMessage `json:"pub_key"`
|
||||
Power int64 `json:"power,string"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func (g GenesisValidator) MarshalJSON() ([]byte, error) {
|
||||
@@ -39,19 +45,30 @@ func (g GenesisValidator) MarshalJSON() ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(struct {
|
||||
Address Address `json:"address"`
|
||||
PubKey json.RawMessage `json:"pub_key"`
|
||||
Power int64 `json:"power,string"`
|
||||
Name string `json:"name"`
|
||||
}{Address: g.Address, PubKey: pk, Power: g.Power, Name: g.Name})
|
||||
return json.Marshal(genesisValidatorJSON{
|
||||
Address: g.Address, PubKey: pk, Power: g.Power, Name: g.Name,
|
||||
})
|
||||
}
|
||||
|
||||
func (g *GenesisValidator) UnmarshalJSON(data []byte) error {
|
||||
var gv genesisValidatorJSON
|
||||
if err := json.Unmarshal(data, &gv); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := jsontypes.Unmarshal(gv.PubKey, &g.PubKey); err != nil {
|
||||
return err
|
||||
}
|
||||
g.Address = gv.Address
|
||||
g.Power = gv.Power
|
||||
g.Name = gv.Name
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenesisDoc defines the initial conditions for a tendermint blockchain, in particular its validator set.
|
||||
type GenesisDoc struct {
|
||||
GenesisTime time.Time `json:"genesis_time"`
|
||||
ChainID string `json:"chain_id"`
|
||||
InitialHeight int64 `json:"initial_height"`
|
||||
InitialHeight int64 `json:"initial_height,string"`
|
||||
ConsensusParams *ConsensusParams `json:"consensus_params,omitempty"`
|
||||
Validators []GenesisValidator `json:"validators,omitempty"`
|
||||
AppHash tmbytes.HexBytes `json:"app_hash"`
|
||||
@@ -60,7 +77,7 @@ type GenesisDoc struct {
|
||||
|
||||
// SaveAs is a utility method for saving GenensisDoc as a JSON file.
|
||||
func (genDoc *GenesisDoc) SaveAs(file string) error {
|
||||
genDocBytes, err := tmjson.MarshalIndent(genDoc, "", " ")
|
||||
genDocBytes, err := json.MarshalIndent(genDoc, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -125,7 +142,7 @@ func (genDoc *GenesisDoc) ValidateAndComplete() error {
|
||||
// GenesisDocFromJSON unmarshalls JSON data into a GenesisDoc.
|
||||
func GenesisDocFromJSON(jsonBlob []byte) (*GenesisDoc, error) {
|
||||
genDoc := GenesisDoc{}
|
||||
err := tmjson.Unmarshal(jsonBlob, &genDoc)
|
||||
err := json.Unmarshal(jsonBlob, &genDoc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@@ -8,7 +9,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
tmtime "github.com/tendermint/tendermint/libs/time"
|
||||
)
|
||||
|
||||
@@ -87,7 +87,7 @@ func TestBasicGenesisDoc(t *testing.T) {
|
||||
ChainID: "abc",
|
||||
Validators: []GenesisValidator{{pubkey.Address(), pubkey, 10, "myval"}},
|
||||
}
|
||||
genDocBytes, err = tmjson.Marshal(baseGenDoc)
|
||||
genDocBytes, err = json.Marshal(baseGenDoc)
|
||||
assert.NoError(t, err, "error marshaling genDoc")
|
||||
|
||||
// test base gendoc and check consensus params were filled
|
||||
@@ -99,15 +99,15 @@ func TestBasicGenesisDoc(t *testing.T) {
|
||||
assert.NotNil(t, genDoc.Validators[0].Address, "expected validator's address to be filled in")
|
||||
|
||||
// create json with consensus params filled
|
||||
genDocBytes, err = tmjson.Marshal(genDoc)
|
||||
genDocBytes, err = json.Marshal(genDoc)
|
||||
assert.NoError(t, err, "error marshaling genDoc")
|
||||
genDoc, err = GenesisDocFromJSON(genDocBytes)
|
||||
require.NoError(t, err, "expected no error for valid genDoc json")
|
||||
|
||||
// test with invalid consensus params
|
||||
genDoc.ConsensusParams.Block.MaxBytes = 0
|
||||
genDocBytes, err = tmjson.Marshal(genDoc)
|
||||
require.NoError(t, err, "error marshaling genDoc")
|
||||
genDocBytes, err = json.Marshal(genDoc)
|
||||
assert.NoError(t, err, "error marshaling genDoc")
|
||||
_, err = GenesisDocFromJSON(genDocBytes)
|
||||
assert.Error(t, err, "expected error for genDoc json with block size of 0")
|
||||
|
||||
|
||||
+3
-3
@@ -24,9 +24,9 @@ func MaxNodeInfoSize() int {
|
||||
|
||||
// ProtocolVersion contains the protocol versions for the software.
|
||||
type ProtocolVersion struct {
|
||||
P2P uint64 `json:"p2p"`
|
||||
Block uint64 `json:"block"`
|
||||
App uint64 `json:"app"`
|
||||
P2P uint64 `json:"p2p,string"`
|
||||
Block uint64 `json:"block,string"`
|
||||
App uint64 `json:"app,string"`
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------
|
||||
|
||||
+25
-9
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
"github.com/tendermint/tendermint/internal/jsontypes"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
tmos "github.com/tendermint/tendermint/libs/os"
|
||||
)
|
||||
|
||||
@@ -19,9 +18,14 @@ import (
|
||||
// It contains the nodes private key for authentication.
|
||||
type NodeKey struct {
|
||||
// Canonical ID - hex-encoded pubkey's address (IDByteLength bytes)
|
||||
ID NodeID `json:"id"`
|
||||
ID NodeID
|
||||
// Private key
|
||||
PrivKey crypto.PrivKey `json:"priv_key"`
|
||||
PrivKey crypto.PrivKey
|
||||
}
|
||||
|
||||
type nodeKeyJSON struct {
|
||||
ID NodeID `json:"id"`
|
||||
PrivKey json.RawMessage `json:"priv_key"`
|
||||
}
|
||||
|
||||
func (nk NodeKey) MarshalJSON() ([]byte, error) {
|
||||
@@ -29,10 +33,22 @@ func (nk NodeKey) MarshalJSON() ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(struct {
|
||||
ID NodeID `json:"id"`
|
||||
PrivKey json.RawMessage `json:"priv_key"`
|
||||
}{ID: nk.ID, PrivKey: pk})
|
||||
return json.Marshal(nodeKeyJSON{
|
||||
ID: nk.ID, PrivKey: pk,
|
||||
})
|
||||
}
|
||||
|
||||
func (nk *NodeKey) UnmarshalJSON(data []byte) error {
|
||||
var nkjson nodeKeyJSON
|
||||
if err := json.Unmarshal(data, &nkjson); err != nil {
|
||||
return err
|
||||
}
|
||||
var pk crypto.PrivKey
|
||||
if err := jsontypes.Unmarshal(nkjson.PrivKey, &pk); err != nil {
|
||||
return err
|
||||
}
|
||||
*nk = NodeKey{ID: nkjson.ID, PrivKey: pk}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PubKey returns the peer's PubKey
|
||||
@@ -42,7 +58,7 @@ func (nk NodeKey) PubKey() crypto.PubKey {
|
||||
|
||||
// SaveAs persists the NodeKey to filePath.
|
||||
func (nk NodeKey) SaveAs(filePath string) error {
|
||||
jsonBytes, err := tmjson.Marshal(nk)
|
||||
jsonBytes, err := json.Marshal(nk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -85,7 +101,7 @@ func LoadNodeKey(filePath string) (NodeKey, error) {
|
||||
return NodeKey{}, err
|
||||
}
|
||||
nodeKey := NodeKey{}
|
||||
err = tmjson.Unmarshal(jsonBytes, &nodeKey)
|
||||
err = json.Unmarshal(jsonBytes, &nodeKey)
|
||||
if err != nil {
|
||||
return NodeKey{}, err
|
||||
}
|
||||
|
||||
+8
-8
@@ -55,15 +55,15 @@ type HashedParams struct {
|
||||
// BlockParams define limits on the block size and gas plus minimum time
|
||||
// between blocks.
|
||||
type BlockParams struct {
|
||||
MaxBytes int64 `json:"max_bytes"`
|
||||
MaxGas int64 `json:"max_gas"`
|
||||
MaxBytes int64 `json:"max_bytes,string"`
|
||||
MaxGas int64 `json:"max_gas,string"`
|
||||
}
|
||||
|
||||
// EvidenceParams determine how we handle evidence of malfeasance.
|
||||
type EvidenceParams struct {
|
||||
MaxAgeNumBlocks int64 `json:"max_age_num_blocks"` // only accept new evidence more recent than this
|
||||
MaxAgeDuration time.Duration `json:"max_age_duration"`
|
||||
MaxBytes int64 `json:"max_bytes"`
|
||||
MaxAgeNumBlocks int64 `json:"max_age_num_blocks,string"` // only accept new evidence more recent than this
|
||||
MaxAgeDuration time.Duration `json:"max_age_duration,string"`
|
||||
MaxBytes int64 `json:"max_bytes,string"`
|
||||
}
|
||||
|
||||
// ValidatorParams restrict the public key types validators can use.
|
||||
@@ -73,14 +73,14 @@ type ValidatorParams struct {
|
||||
}
|
||||
|
||||
type VersionParams struct {
|
||||
AppVersion uint64 `json:"app_version"`
|
||||
AppVersion uint64 `json:"app_version,string"`
|
||||
}
|
||||
|
||||
// SynchronyParams influence the validity of block timestamps.
|
||||
// TODO (@wbanfield): add link to proposer-based timestamp spec when completed.
|
||||
type SynchronyParams struct {
|
||||
Precision time.Duration `json:"precision"`
|
||||
MessageDelay time.Duration `json:"message_delay"`
|
||||
Precision time.Duration `json:"precision,string"`
|
||||
MessageDelay time.Duration `json:"message_delay,string"`
|
||||
}
|
||||
|
||||
// DefaultConsensusParams returns a default ConsensusParams.
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ var (
|
||||
// If POLRound >= 0, then BlockID corresponds to the block that is locked in POLRound.
|
||||
type Proposal struct {
|
||||
Type tmproto.SignedMsgType
|
||||
Height int64 `json:"height"`
|
||||
Height int64 `json:"height,string"`
|
||||
Round int32 `json:"round"` // there can not be greater than 2_147_483_647 rounds
|
||||
POLRound int32 `json:"pol_round"` // -1 if null.
|
||||
BlockID BlockID `json:"block_id"`
|
||||
|
||||
+37
-13
@@ -17,23 +17,47 @@ import (
|
||||
// NOTE: The ProposerPriority is not included in Validator.Hash();
|
||||
// make sure to update that method if changes are made here
|
||||
type Validator struct {
|
||||
Address Address `json:"address"`
|
||||
PubKey crypto.PubKey `json:"pub_key"`
|
||||
VotingPower int64 `json:"voting_power,string"`
|
||||
ProposerPriority int64 `json:"proposer_priority,string"`
|
||||
Address Address
|
||||
PubKey crypto.PubKey
|
||||
VotingPower int64
|
||||
ProposerPriority int64
|
||||
}
|
||||
|
||||
type validatorJSON struct {
|
||||
Address Address `json:"address"`
|
||||
PubKey json.RawMessage `json:"pub_key,omitempty"`
|
||||
VotingPower int64 `json:"voting_power,string"`
|
||||
ProposerPriority int64 `json:"proposer_priority,string"`
|
||||
}
|
||||
|
||||
func (v Validator) MarshalJSON() ([]byte, error) {
|
||||
pk, err := jsontypes.Marshal(v.PubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
val := validatorJSON{
|
||||
Address: v.Address,
|
||||
VotingPower: v.VotingPower,
|
||||
ProposerPriority: v.ProposerPriority,
|
||||
}
|
||||
return json.Marshal(struct {
|
||||
Addr Address `json:"address"`
|
||||
PubKey json.RawMessage `json:"pub_key"`
|
||||
Power int64 `json:"voting_power,string"`
|
||||
Priority int64 `json:"proposer_priority,string"`
|
||||
}{Addr: v.Address, PubKey: pk, Power: v.VotingPower, Priority: v.ProposerPriority})
|
||||
if v.PubKey != nil {
|
||||
pk, err := jsontypes.Marshal(v.PubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
val.PubKey = pk
|
||||
}
|
||||
return json.Marshal(val)
|
||||
}
|
||||
|
||||
func (v *Validator) UnmarshalJSON(data []byte) error {
|
||||
var val validatorJSON
|
||||
if err := json.Unmarshal(data, &val); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := jsontypes.Unmarshal(val.PubKey, &v.PubKey); err != nil {
|
||||
return err
|
||||
}
|
||||
v.Address = val.Address
|
||||
v.VotingPower = val.VotingPower
|
||||
v.ProposerPriority = val.ProposerPriority
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewValidator returns a new validator with the given pubkey and voting power.
|
||||
|
||||
Reference in New Issue
Block a user