diff --git a/Gopkg.lock b/Gopkg.lock index 70be9a3fa..4ab26b6c9 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -224,6 +224,7 @@ revision = "34011bf325bce385408353a30b101fe5e923eb6e" [[projects]] + branch = "develop" name = "github.com/tendermint/abci" packages = [ "client", @@ -233,7 +234,7 @@ "server", "types" ] - revision = "345a5a5a34aa31ad00626266f59e4ae7652ee274" + revision = "9e0e00bef42aebf6b402f66bf0f3dc607de8a6f3" [[projects]] branch = "master" @@ -248,8 +249,8 @@ [[projects]] name = "github.com/tendermint/go-crypto" packages = ["."] - revision = "dd20358a264c772b4a83e477b0cfce4c88a7001d" - version = "v0.4.1" + revision = "c3e19f3ea26f5c3357e0bcbb799b0761ef923755" + version = "v0.5.0" [[projects]] name = "github.com/tendermint/go-wire" @@ -257,8 +258,8 @@ ".", "data" ] - revision = "b6fc872b42d41158a60307db4da051dd6f179415" - version = "v0.7.2" + revision = "fa721242b042ecd4c6ed1a934ee740db4f74e45c" + version = "v0.7.3" [[projects]] name = "github.com/tendermint/tmlibs" @@ -371,6 +372,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "a65ef86e3db67769c1fa4ae1f67af8d5b95474f4b95ec799d8572e19b44b206a" + inputs-digest = "36505c5f341e1e80d7d55589a17727bbd9e89403624b4e02c9e8a21b1ed39d0f" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 73ab28cbd..cdc31302b 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -71,15 +71,15 @@ [[constraint]] name = "github.com/tendermint/abci" - revision = "345a5a5a34aa31ad00626266f59e4ae7652ee274" + branch = "develop" [[constraint]] name = "github.com/tendermint/go-crypto" - version = "0.4.1" + version = "0.5.0" [[constraint]] name = "github.com/tendermint/go-wire" - version = "0.7.2" + version = "0.7.3" [[constraint]] name = "github.com/tendermint/tmlibs" diff --git a/consensus/byzantine_test.go b/consensus/byzantine_test.go index 38bb37474..0d817215d 100644 --- a/consensus/byzantine_test.go +++ b/consensus/byzantine_test.go @@ -289,17 +289,17 @@ func (privVal *ByzantinePrivValidator) GetPubKey() crypto.PubKey { } func (privVal *ByzantinePrivValidator) SignVote(chainID string, vote *types.Vote) (err error) { - vote.Signature, err = privVal.Sign(types.SignBytes(chainID, vote)) + vote.Signature, err = privVal.Sign(vote.SignBytes(chainID)) return err } func (privVal *ByzantinePrivValidator) SignProposal(chainID string, proposal *types.Proposal) (err error) { - proposal.Signature, _ = privVal.Sign(types.SignBytes(chainID, proposal)) + proposal.Signature, _ = privVal.Sign(proposal.SignBytes(chainID)) return nil } func (privVal *ByzantinePrivValidator) SignHeartbeat(chainID string, heartbeat *types.Heartbeat) (err error) { - heartbeat.Signature, _ = privVal.Sign(types.SignBytes(chainID, heartbeat)) + heartbeat.Signature, _ = privVal.Sign(heartbeat.SignBytes(chainID)) return nil } diff --git a/consensus/replay.go b/consensus/replay.go index 2e6b36a22..39dd592a4 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -2,6 +2,7 @@ package consensus import ( "bytes" + "encoding/json" "fmt" "hash/crc32" "io" @@ -190,13 +191,23 @@ type Handshaker struct { stateDB dbm.DB initialState sm.State store types.BlockStore + appState json.RawMessage logger log.Logger nBlocks int // number of blocks applied to the state } -func NewHandshaker(stateDB dbm.DB, state sm.State, store types.BlockStore) *Handshaker { - return &Handshaker{stateDB, state, store, log.NewNopLogger(), 0} +func NewHandshaker(stateDB dbm.DB, state sm.State, + store types.BlockStore, appState json.RawMessage) *Handshaker { + + return &Handshaker{ + stateDB: stateDB, + initialState: state, + store: store, + appState: appState, + logger: log.NewNopLogger(), + nBlocks: 0, + } } func (h *Handshaker) SetLogger(l log.Logger) { @@ -249,9 +260,12 @@ func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight // If appBlockHeight == 0 it means that we are at genesis and hence should send InitChain if appBlockHeight == 0 { validators := types.TM2PB.Validators(state.Validators) - // TODO: get the genesis bytes (https://github.com/tendermint/tendermint/issues/1224) - var genesisBytes []byte - if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{validators, genesisBytes}); err != nil { + req := abci.RequestInitChain{ + Validators: validators, + AppStateBytes: h.appState, + } + _, err := proxyApp.Consensus().InitChainSync(req) + if err != nil { return nil, err } } diff --git a/consensus/replay_file.go b/consensus/replay_file.go index 979425f87..d47e90527 100644 --- a/consensus/replay_file.go +++ b/consensus/replay_file.go @@ -287,14 +287,19 @@ func newConsensusStateForReplay(config cfg.BaseConfig, csConfig *cfg.ConsensusCo // Get State stateDB := dbm.NewDB("state", dbType, config.DBDir()) - state, err := sm.MakeGenesisStateFromFile(config.GenesisFile()) + gdoc, err := sm.MakeGenesisDocFromFile(config.GenesisFile()) + if err != nil { + cmn.Exit(err.Error()) + } + state, err := sm.MakeGenesisState(gdoc) if err != nil { cmn.Exit(err.Error()) } // Create proxyAppConn connection (consensus, mempool, query) clientCreator := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()) - proxyApp := proxy.NewAppConns(clientCreator, NewHandshaker(stateDB, state, blockStore)) + proxyApp := proxy.NewAppConns(clientCreator, + NewHandshaker(stateDB, state, blockStore, gdoc.AppState)) err = proxyApp.Start() if err != nil { cmn.Exit(cmn.Fmt("Error starting proxy app conns: %v", err)) diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 9bcddeaee..9d7bbc8e4 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -362,7 +362,7 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) { } // now start the app using the handshake - it should sync - handshaker := NewHandshaker(stateDB, state, store) + handshaker := NewHandshaker(stateDB, state, store, nil) proxyApp := proxy.NewAppConns(clientCreator2, handshaker) if err := proxyApp.Start(); err != nil { t.Fatalf("Error starting proxy app connections: %v", err) diff --git a/consensus/state.go b/consensus/state.go index 62d1c925b..30bd56f10 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -1269,7 +1269,7 @@ func (cs *ConsensusState) defaultSetProposal(proposal *types.Proposal) error { } // Verify signature - if !cs.Validators.GetProposer().PubKey.VerifyBytes(types.SignBytes(cs.state.ChainID, proposal), proposal.Signature) { + if !cs.Validators.GetProposer().PubKey.VerifyBytes(proposal.SignBytes(cs.state.ChainID), proposal.Signature) { return ErrInvalidProposalSignature } diff --git a/consensus/types/height_vote_set.go b/consensus/types/height_vote_set.go index 17ef334db..7db932045 100644 --- a/consensus/types/height_vote_set.go +++ b/consensus/types/height_vote_set.go @@ -218,5 +218,5 @@ func (hvs *HeightVoteSet) SetPeerMaj23(round int, type_ byte, peerID p2p.ID, blo if voteSet == nil { return nil // something we don't know about yet } - return voteSet.SetPeerMaj23(peerID, blockID) + return voteSet.SetPeerMaj23(types.P2PID(peerID), blockID) } diff --git a/consensus/wal_generator.go b/consensus/wal_generator.go index dee8b5143..14f82d8a8 100644 --- a/consensus/wal_generator.go +++ b/consensus/wal_generator.go @@ -52,7 +52,7 @@ func WALWithNBlocks(numBlocks int) (data []byte, err error) { return nil, errors.Wrap(err, "failed to make genesis state") } blockStore := bc.NewBlockStore(blockStoreDB) - handshaker := NewHandshaker(stateDB, state, blockStore) + handshaker := NewHandshaker(stateDB, state, blockStore, genDoc.AppState) proxyApp := proxy.NewAppConns(proxy.NewLocalClientCreator(app), handshaker) proxyApp.SetLogger(logger.With("module", "proxy")) if err := proxyApp.Start(); err != nil { diff --git a/lite/helpers.go b/lite/helpers.go index d985882de..7df77027c 100644 --- a/lite/helpers.go +++ b/lite/helpers.go @@ -102,7 +102,7 @@ func makeVote(header *types.Header, vals *types.ValidatorSet, key crypto.PrivKey BlockID: types.BlockID{Hash: header.Hash()}, } // Sign it - signBytes := types.SignBytes(header.ChainID, vote) + signBytes := vote.SignBytes(header.ChainID) vote.Signature = key.Sign(signBytes) return vote } diff --git a/node/node.go b/node/node.go index 710a81dc4..9c78360af 100644 --- a/node/node.go +++ b/node/node.go @@ -162,7 +162,7 @@ func NewNode(config *cfg.Config, // and sync tendermint and the app by performing a handshake // and replaying any necessary blocks consensusLogger := logger.With("module", "consensus") - handshaker := cs.NewHandshaker(stateDB, state, blockStore) + handshaker := cs.NewHandshaker(stateDB, state, blockStore, genDoc.AppState) handshaker.SetLogger(consensusLogger) proxyApp := proxy.NewAppConns(clientCreator, handshaker) proxyApp.SetLogger(logger.With("module", "proxy")) diff --git a/rpc/lib/client/http_client.go b/rpc/lib/client/http_client.go index b0bff02b9..902b7eebc 100644 --- a/rpc/lib/client/http_client.go +++ b/rpc/lib/client/http_client.go @@ -187,7 +187,6 @@ func argsToJson(args map[string]interface{}) error { continue } - // Pass everything else to go-wire data, err := json.Marshal(v) if err != nil { return err diff --git a/state/state.go b/state/state.go index 575a1630e..fb5d78c47 100644 --- a/state/state.go +++ b/state/state.go @@ -86,7 +86,11 @@ func (s State) Equals(s2 State) bool { // Bytes serializes the State using go-wire. func (s State) Bytes() []byte { - return wire.BinaryBytes(s) + bz, err := wire.MarshalBinary(s) + if err != nil { + panic(err) + } + return bz } // IsEmpty returns true if the State is equal to the empty State. diff --git a/state/store.go b/state/store.go index de2d4d67c..df07ec540 100644 --- a/state/store.go +++ b/state/store.go @@ -1,7 +1,6 @@ package state import ( - "bytes" "fmt" abci "github.com/tendermint/abci/types" @@ -70,12 +69,11 @@ func loadState(db dbm.DB, key []byte) (state State) { return state } - r, n, err := bytes.NewReader(buf), new(int), new(error) - wire.ReadBinaryPtr(&state, r, 0, n, err) - if *err != nil { + err := wire.UnmarshalBinary(buf, &state) + if err != nil { // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED cmn.Exit(cmn.Fmt(`LoadState: Data has been corrupted or its spec has changed: - %v\n`, *err)) + %v\n`, err)) } // TODO: ensure that buf is completely read. @@ -113,7 +111,11 @@ func NewABCIResponses(block *types.Block) *ABCIResponses { // Bytes serializes the ABCIResponse using go-wire func (a *ABCIResponses) Bytes() []byte { - return wire.BinaryBytes(*a) + bz, err := wire.MarshalBinary(*a) + if err != nil { + panic(err) + } + return bz } func (a *ABCIResponses) ResultsHash() []byte { @@ -131,12 +133,11 @@ func LoadABCIResponses(db dbm.DB, height int64) (*ABCIResponses, error) { } abciResponses := new(ABCIResponses) - r, n, err := bytes.NewReader(buf), new(int), new(error) - wire.ReadBinaryPtr(abciResponses, r, 0, n, err) - if *err != nil { + err := wire.UnmarshalBinary(buf, abciResponses) + if err != nil { // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED cmn.Exit(cmn.Fmt(`LoadABCIResponses: Data has been corrupted or its spec has - changed: %v\n`, *err)) + changed: %v\n`, err)) } // TODO: ensure that buf is completely read. @@ -160,7 +161,11 @@ type ValidatorsInfo struct { // Bytes serializes the ValidatorsInfo using go-wire func (valInfo *ValidatorsInfo) Bytes() []byte { - return wire.BinaryBytes(*valInfo) + bz, err := wire.MarshalBinary(*valInfo) + if err != nil { + panic(err) + } + return bz } // LoadValidators loads the ValidatorSet for a given height. @@ -189,12 +194,11 @@ func loadValidatorsInfo(db dbm.DB, height int64) *ValidatorsInfo { } v := new(ValidatorsInfo) - r, n, err := bytes.NewReader(buf), new(int), new(error) - wire.ReadBinaryPtr(v, r, 0, n, err) - if *err != nil { + err := wire.UnmarshalBinary(buf, v) + if err != nil { // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED cmn.Exit(cmn.Fmt(`LoadValidators: Data has been corrupted or its spec has changed: - %v\n`, *err)) + %v\n`, err)) } // TODO: ensure that buf is completely read. @@ -225,7 +229,11 @@ type ConsensusParamsInfo struct { // Bytes serializes the ConsensusParamsInfo using go-wire func (params ConsensusParamsInfo) Bytes() []byte { - return wire.BinaryBytes(params) + bz, err := wire.MarshalBinary(params) + if err != nil { + panic(err) + } + return bz } // LoadConsensusParams loads the ConsensusParams for a given height. @@ -255,12 +263,11 @@ func loadConsensusParamsInfo(db dbm.DB, height int64) *ConsensusParamsInfo { } paramsInfo := new(ConsensusParamsInfo) - r, n, err := bytes.NewReader(buf), new(int), new(error) - wire.ReadBinaryPtr(paramsInfo, r, 0, n, err) - if *err != nil { + err := wire.UnmarshalBinary(buf, paramsInfo) + if err != nil { // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED cmn.Exit(cmn.Fmt(`LoadConsensusParams: Data has been corrupted or its spec has changed: - %v\n`, *err)) + %v\n`, err)) } // TODO: ensure that buf is completely read. diff --git a/state/txindex/kv/kv.go b/state/txindex/kv/kv.go index ed4fcc8a6..24e982725 100644 --- a/state/txindex/kv/kv.go +++ b/state/txindex/kv/kv.go @@ -67,10 +67,8 @@ func (txi *TxIndex) Get(hash []byte) (*types.TxResult, error) { return nil, nil } - r := bytes.NewReader(rawBytes) - var n int - var err error - txResult := wire.ReadBinary(&types.TxResult{}, r, 0, &n, &err).(*types.TxResult) + txResult := new(types.TxResult) + err := wire.UnmarshalBinary(rawBytes, &txResult) if err != nil { return nil, fmt.Errorf("Error reading TxResult: %v", err) } @@ -93,7 +91,10 @@ func (txi *TxIndex) AddBatch(b *txindex.Batch) error { } // index tx by hash - rawBytes := wire.BinaryBytes(result) + rawBytes, err := wire.MarshalBinary(result) + if err != nil { + return err + } storeBatch.Set(hash, rawBytes) } @@ -115,7 +116,10 @@ func (txi *TxIndex) Index(result *types.TxResult) error { } // index tx by hash - rawBytes := wire.BinaryBytes(result) + rawBytes, err := wire.MarshalBinary(result) + if err != nil { + return err + } b.Set(hash, rawBytes) b.Write() diff --git a/types/block.go b/types/block.go index 3e800e722..53fc6a811 100644 --- a/types/block.go +++ b/types/block.go @@ -4,11 +4,10 @@ import ( "bytes" "errors" "fmt" - "io" "strings" "time" - wire "github.com/tendermint/go-wire" + wire "github.com/tendermint/tendermint/wire" cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/merkle" "golang.org/x/crypto/ripemd160" @@ -96,7 +95,11 @@ func (b *Block) Hash() cmn.HexBytes { // MakePartSet returns a PartSet containing parts of a serialized block. // This is the form in which the block is gossipped to peers. func (b *Block) MakePartSet(partSize int) *PartSet { - return NewPartSetFromData(wire.BinaryBytes(b), partSize) + bz, err := wire.MarshalBinary(b) + if err != nil { + panic(err) + } + return NewPartSetFromData(bz, partSize) } // HashesTo is a convenience function that checks if a block hashes to the given argument. @@ -249,7 +252,8 @@ type Commit struct { bitArray *cmn.BitArray } -// FirstPrecommit returns the first non-nil precommit in the commit +// FirstPrecommit returns the first non-nil precommit in the commit. +// If all precommits are nil, it returns an empty precommit with height 0. func (commit *Commit) FirstPrecommit() *Vote { if len(commit.Precommits) == 0 { return nil @@ -263,7 +267,9 @@ func (commit *Commit) FirstPrecommit() *Vote { return precommit } } - return nil + return &Vote{ + Type: VoteTypePrecommit, + } } // Height returns the height of the commit @@ -493,17 +499,11 @@ func (blockID BlockID) Equals(other BlockID) bool { // Key returns a machine-readable string representation of the BlockID func (blockID BlockID) Key() string { - return string(blockID.Hash) + string(wire.BinaryBytes(blockID.PartsHeader)) -} - -// WriteSignBytes writes the canonical bytes of the BlockID to the given writer for digital signing -func (blockID BlockID) WriteSignBytes(w io.Writer, n *int, err *error) { - if blockID.IsZero() { - wire.WriteTo([]byte("null"), w, n, err) - } else { - wire.WriteJSON(CanonicalBlockID(blockID), w, n, err) + bz, err := wire.MarshalBinary(blockID.PartsHeader) + if err != nil { + panic(err) } - + return string(blockID.Hash) + string(bz) } // String returns a human readable string representation of the BlockID @@ -518,15 +518,24 @@ type hasher struct { } func (h hasher) Hash() []byte { - hasher, n, err := ripemd160.New(), new(int), new(error) - wire.WriteBinary(h.item, hasher, n, err) - if *err != nil { + hasher := ripemd160.New() + bz, err := wire.MarshalBinary(h.item) + if err != nil { + panic(err) + } + _, err = hasher.Write(bz) + if err != nil { panic(err) } return hasher.Sum(nil) } +func tmHash(item interface{}) []byte { + h := hasher{item} + return h.Hash() +} + func wireHasher(item interface{}) merkle.Hasher { return hasher{item} } diff --git a/types/canonical_json.go b/types/canonical_json.go index 879bb5c52..4eeeb2064 100644 --- a/types/canonical_json.go +++ b/types/canonical_json.go @@ -3,11 +3,11 @@ package types import ( "time" - wire "github.com/tendermint/go-wire" + wire "github.com/tendermint/tendermint/wire" cmn "github.com/tendermint/tmlibs/common" ) -// canonical json is go-wire's json for structs with fields in alphabetical order +// canonical json is wire's json for structs with fields in alphabetical order // TimeFormat is used for generating the sigs const TimeFormat = wire.RFC3339Millis @@ -114,7 +114,7 @@ func CanonicalHeartbeat(heartbeat *Heartbeat) CanonicalJSONHeartbeat { } func CanonicalTime(t time.Time) string { - // note that sending time over go-wire resets it to + // note that sending time over wire resets it to // local time, we need to force UTC here, so the // signatures match return t.UTC().Format(TimeFormat) diff --git a/types/evidence.go b/types/evidence.go index 9973c62e6..9e1f6af0e 100644 --- a/types/evidence.go +++ b/types/evidence.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/tendermint/go-crypto" - wire "github.com/tendermint/go-wire" + wire "github.com/tendermint/tendermint/wire" "github.com/tendermint/tmlibs/merkle" ) @@ -148,10 +148,10 @@ func (dve *DuplicateVoteEvidence) Verify(chainID string) error { } // Signatures must be valid - if !dve.PubKey.VerifyBytes(SignBytes(chainID, dve.VoteA), dve.VoteA.Signature) { + if !dve.PubKey.VerifyBytes(dve.VoteA.SignBytes(chainID), dve.VoteA.Signature) { return fmt.Errorf("DuplicateVoteEvidence Error verifying VoteA: %v", ErrVoteInvalidSignature) } - if !dve.PubKey.VerifyBytes(SignBytes(chainID, dve.VoteB), dve.VoteB.Signature) { + if !dve.PubKey.VerifyBytes(dve.VoteB.SignBytes(chainID), dve.VoteB.Signature) { return fmt.Errorf("DuplicateVoteEvidence Error verifying VoteB: %v", ErrVoteInvalidSignature) } diff --git a/types/evidence_test.go b/types/evidence_test.go index 876b68ad1..84811514a 100644 --- a/types/evidence_test.go +++ b/types/evidence_test.go @@ -22,7 +22,7 @@ func makeVote(val *PrivValidatorFS, chainID string, valIndex int, height int64, Type: byte(step), BlockID: blockID, } - sig := val.PrivKey.Sign(SignBytes(chainID, v)) + sig := val.PrivKey.Sign(v.SignBytes(chainID)) v.Signature = sig return v @@ -41,7 +41,7 @@ func TestEvidence(t *testing.T) { vote1 := makeVote(val, chainID, 0, 10, 2, 1, blockID) badVote := makeVote(val, chainID, 0, 10, 2, 1, blockID) - badVote.Signature = val2.PrivKey.Sign(SignBytes(chainID, badVote)) + badVote.Signature = val2.PrivKey.Sign(badVote.SignBytes(chainID)) cases := []voteData{ {vote1, makeVote(val, chainID, 0, 10, 2, 1, blockID2), true}, // different block ids diff --git a/types/heartbeat.go b/types/heartbeat.go index a4ff0d217..fc5f8ad7f 100644 --- a/types/heartbeat.go +++ b/types/heartbeat.go @@ -2,10 +2,9 @@ package types import ( "fmt" - "io" "github.com/tendermint/go-crypto" - "github.com/tendermint/go-wire" + "github.com/tendermint/tendermint/wire" cmn "github.com/tendermint/tmlibs/common" ) @@ -23,13 +22,17 @@ type Heartbeat struct { Signature crypto.Signature `json:"signature"` } -// WriteSignBytes writes the Heartbeat for signing. +// SignBytes returns the Heartbeat bytes for signing. // It panics if the Heartbeat is nil. -func (heartbeat *Heartbeat) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) { - wire.WriteJSON(CanonicalJSONOnceHeartbeat{ +func (heartbeat *Heartbeat) SignBytes(chainID string) []byte { + bz, err := wire.MarshalJSON(CanonicalJSONOnceHeartbeat{ chainID, CanonicalHeartbeat(heartbeat), - }, w, n, err) + }) + if err != nil { + panic(err) + } + return bz } // Copy makes a copy of the Heartbeat. diff --git a/types/heartbeat_test.go b/types/heartbeat_test.go index 660ccd0f9..206636166 100644 --- a/types/heartbeat_test.go +++ b/types/heartbeat_test.go @@ -1,7 +1,6 @@ package types import ( - "bytes" "testing" "github.com/stretchr/testify/require" @@ -34,23 +33,18 @@ func TestHeartbeatString(t *testing.T) { } func TestHeartbeatWriteSignBytes(t *testing.T) { - var n int - var err error - buf := new(bytes.Buffer) hb := &Heartbeat{ValidatorIndex: 1, Height: 10, Round: 1} - hb.WriteSignBytes("0xdeadbeef", buf, &n, &err) - require.Equal(t, buf.String(), `{"chain_id":"0xdeadbeef","heartbeat":{"height":10,"round":1,"sequence":0,"validator_address":"","validator_index":1}}`) + bz := hb.SignBytes("0xdeadbeef") + require.Equal(t, string(bz), `{"chain_id":"0xdeadbeef","heartbeat":{"height":10,"round":1,"sequence":0,"validator_address":"","validator_index":1}}`) - buf.Reset() plainHb := &Heartbeat{} - plainHb.WriteSignBytes("0xdeadbeef", buf, &n, &err) - require.Equal(t, buf.String(), `{"chain_id":"0xdeadbeef","heartbeat":{"height":0,"round":0,"sequence":0,"validator_address":"","validator_index":0}}`) + bz = plainHb.SignBytes("0xdeadbeef") + require.Equal(t, string(bz), `{"chain_id":"0xdeadbeef","heartbeat":{"height":0,"round":0,"sequence":0,"validator_address":"","validator_index":0}}`) require.Panics(t, func() { - buf.Reset() var nilHb *Heartbeat - nilHb.WriteSignBytes("0xdeadbeef", buf, &n, &err) - require.Equal(t, buf.String(), "null") + bz := nilHb.SignBytes("0xdeadbeef") + require.Equal(t, string(bz), "null") }) } diff --git a/types/part_set.go b/types/part_set.go index ff11f7d35..5c43b1518 100644 --- a/types/part_set.go +++ b/types/part_set.go @@ -9,7 +9,6 @@ import ( "golang.org/x/crypto/ripemd160" - "github.com/tendermint/go-wire" cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/merkle" ) @@ -73,10 +72,6 @@ func (psh PartSetHeader) Equals(other PartSetHeader) bool { return psh.Total == other.Total && bytes.Equal(psh.Hash, other.Hash) } -func (psh PartSetHeader) WriteSignBytes(w io.Writer, n *int, err *error) { - wire.WriteJSON(CanonicalPartSetHeader(psh), w, n, err) -} - //------------------------------------- type PartSet struct { diff --git a/types/priv_validator.go b/types/priv_validator.go index bf370a88a..fe1a5d1cf 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -234,10 +234,11 @@ func (privVal *PrivValidatorFS) save() { // Reset resets all fields in the PrivValidatorFS. // NOTE: Unsafe! func (privVal *PrivValidatorFS) Reset() { + var sig crypto.Signature privVal.LastHeight = 0 privVal.LastRound = 0 privVal.LastStep = 0 - privVal.LastSignature = crypto.Signature{} + privVal.LastSignature = sig privVal.LastSignBytes = nil privVal.Save() } @@ -297,7 +298,7 @@ func (privVal *PrivValidatorFS) checkHRS(height int64, round int, step int8) (bo // a previously signed vote (ie. we crashed after signing but before the vote hit the WAL). func (privVal *PrivValidatorFS) signVote(chainID string, vote *Vote) error { height, round, step := vote.Height, vote.Round, voteToStep(vote) - signBytes := SignBytes(chainID, vote) + signBytes := vote.SignBytes(chainID) sameHRS, err := privVal.checkHRS(height, round, step) if err != nil { @@ -336,7 +337,7 @@ func (privVal *PrivValidatorFS) signVote(chainID string, vote *Vote) error { // a previously signed proposal ie. we crashed after signing but before the proposal hit the WAL). func (privVal *PrivValidatorFS) signProposal(chainID string, proposal *Proposal) error { height, round, step := proposal.Height, proposal.Round, stepPropose - signBytes := SignBytes(chainID, proposal) + signBytes := proposal.SignBytes(chainID) sameHRS, err := privVal.checkHRS(height, round, step) if err != nil { @@ -388,7 +389,7 @@ func (privVal *PrivValidatorFS) SignHeartbeat(chainID string, heartbeat *Heartbe privVal.mtx.Lock() defer privVal.mtx.Unlock() var err error - heartbeat.Signature, err = privVal.Sign(SignBytes(chainID, heartbeat)) + heartbeat.Signature, err = privVal.Sign(heartbeat.SignBytes(chainID)) return err } diff --git a/types/priv_validator/priv_validator_test.go b/types/priv_validator/priv_validator_test.go index 664d59cf2..120a0c86e 100644 --- a/types/priv_validator/priv_validator_test.go +++ b/types/priv_validator/priv_validator_test.go @@ -211,7 +211,7 @@ func TestDifferByTimestamp(t *testing.T) { proposal := newProposal(height, round, block1) err := privVal.SignProposal(chainID, proposal) assert.NoError(t, err, "expected no error signing proposal") - signBytes := types.SignBytes(chainID, proposal) + signBytes := proposal.SignBytes(chainID) sig := proposal.Signature timeStamp := clipToMS(proposal.Timestamp) @@ -222,7 +222,7 @@ func TestDifferByTimestamp(t *testing.T) { assert.NoError(t, err, "expected no error on signing same proposal") assert.Equal(t, timeStamp, proposal.Timestamp) - assert.Equal(t, signBytes, types.SignBytes(chainID, proposal)) + assert.Equal(t, signBytes, proposal.SignBytes(chainID)) assert.Equal(t, sig, proposal.Signature) } @@ -237,7 +237,7 @@ func TestDifferByTimestamp(t *testing.T) { err = privVal.SignVote("mychainid", vote) assert.NoError(t, err, "expected no error signing vote") - signBytes := types.SignBytes(chainID, vote) + signBytes := vote.SignBytes(chainID) sig := vote.Signature timeStamp := clipToMS(vote.Timestamp) @@ -248,7 +248,7 @@ func TestDifferByTimestamp(t *testing.T) { assert.NoError(t, err, "expected no error on signing same vote") assert.Equal(t, timeStamp, vote.Timestamp) - assert.Equal(t, signBytes, types.SignBytes(chainID, vote)) + assert.Equal(t, signBytes, vote.SignBytes(chainID)) assert.Equal(t, sig, vote.Signature) } } diff --git a/types/priv_validator/sign_info.go b/types/priv_validator/sign_info.go index 60113c570..746131a96 100644 --- a/types/priv_validator/sign_info.go +++ b/types/priv_validator/sign_info.go @@ -112,7 +112,7 @@ func (info *LastSignedInfo) Reset() { // Else it returns an error. func (lsi *LastSignedInfo) SignVote(signer types.Signer, chainID string, vote *types.Vote) error { height, round, step := vote.Height, vote.Round, voteToStep(vote) - signBytes := types.SignBytes(chainID, vote) + signBytes := vote.SignBytes(chainID) sameHRS, err := lsi.Verify(height, round, step) if err != nil { @@ -151,7 +151,7 @@ func (lsi *LastSignedInfo) SignVote(signer types.Signer, chainID string, vote *t // Else it returns an error. func (lsi *LastSignedInfo) SignProposal(signer types.Signer, chainID string, proposal *types.Proposal) error { height, round, step := proposal.Height, proposal.Round, stepPropose - signBytes := types.SignBytes(chainID, proposal) + signBytes := proposal.SignBytes(chainID) sameHRS, err := lsi.Verify(height, round, step) if err != nil { diff --git a/types/priv_validator/unencrypted.go b/types/priv_validator/unencrypted.go index 3ef38eeca..10a304d9e 100644 --- a/types/priv_validator/unencrypted.go +++ b/types/priv_validator/unencrypted.go @@ -61,6 +61,6 @@ func (upv *PrivValidatorUnencrypted) SignProposal(chainID string, proposal *type func (upv *PrivValidatorUnencrypted) SignHeartbeat(chainID string, heartbeat *types.Heartbeat) error { var err error - heartbeat.Signature, err = upv.PrivKey.Sign(types.SignBytes(chainID, heartbeat)) + heartbeat.Signature, err = upv.PrivKey.Sign(heartbeat.SignBytes(chainID)) return err } diff --git a/types/priv_validator_test.go b/types/priv_validator_test.go index 08b58273a..edfcdf58c 100644 --- a/types/priv_validator_test.go +++ b/types/priv_validator_test.go @@ -185,18 +185,19 @@ func TestDifferByTimestamp(t *testing.T) { proposal := newProposal(height, round, block1) err := privVal.SignProposal(chainID, proposal) assert.NoError(t, err, "expected no error signing proposal") - signBytes := SignBytes(chainID, proposal) + signBytes := proposal.SignBytes(chainID) sig := proposal.Signature timeStamp := clipToMS(proposal.Timestamp) // manipulate the timestamp. should get changed back proposal.Timestamp = proposal.Timestamp.Add(time.Millisecond) - proposal.Signature = crypto.Signature{} + var emptySig crypto.Signature + proposal.Signature = emptySig err = privVal.SignProposal("mychainid", proposal) assert.NoError(t, err, "expected no error on signing same proposal") assert.Equal(t, timeStamp, proposal.Timestamp) - assert.Equal(t, signBytes, SignBytes(chainID, proposal)) + assert.Equal(t, signBytes, proposal.SignBytes(chainID)) assert.Equal(t, sig, proposal.Signature) } @@ -208,18 +209,19 @@ func TestDifferByTimestamp(t *testing.T) { err := privVal.SignVote("mychainid", vote) assert.NoError(t, err, "expected no error signing vote") - signBytes := SignBytes(chainID, vote) + signBytes := vote.SignBytes(chainID) sig := vote.Signature timeStamp := clipToMS(vote.Timestamp) // manipulate the timestamp. should get changed back vote.Timestamp = vote.Timestamp.Add(time.Millisecond) - vote.Signature = crypto.Signature{} + var emptySig crypto.Signature + vote.Signature = emptySig err = privVal.SignVote("mychainid", vote) assert.NoError(t, err, "expected no error on signing same vote") assert.Equal(t, timeStamp, vote.Timestamp) - assert.Equal(t, signBytes, SignBytes(chainID, vote)) + assert.Equal(t, signBytes, vote.SignBytes(chainID)) assert.Equal(t, sig, vote.Signature) } } diff --git a/types/proposal.go b/types/proposal.go index 98600681a..c240756bb 100644 --- a/types/proposal.go +++ b/types/proposal.go @@ -3,11 +3,10 @@ package types import ( "errors" "fmt" - "io" "time" "github.com/tendermint/go-crypto" - "github.com/tendermint/go-wire" + "github.com/tendermint/tendermint/wire" ) var ( @@ -50,10 +49,14 @@ func (p *Proposal) String() string { p.POLBlockID, p.Signature, CanonicalTime(p.Timestamp)) } -// WriteSignBytes writes the Proposal bytes for signing -func (p *Proposal) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) { - wire.WriteJSON(CanonicalJSONOnceProposal{ +// SignBytes returns the Proposal bytes for signing +func (p *Proposal) SignBytes(chainID string) []byte { + bz, err := wire.MarshalJSON(CanonicalJSONOnceProposal{ ChainID: chainID, Proposal: CanonicalProposal(p), - }, w, n, err) + }) + if err != nil { + panic(err) + } + return bz } diff --git a/types/proposal_test.go b/types/proposal_test.go index 6fbfbba05..610f76855 100644 --- a/types/proposal_test.go +++ b/types/proposal_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" - wire "github.com/tendermint/go-wire" + wire "github.com/tendermint/tendermint/wire" ) var testProposal *Proposal @@ -26,7 +26,7 @@ func init() { } func TestProposalSignable(t *testing.T) { - signBytes := SignBytes("test_chain_id", testProposal) + signBytes := testProposal.SignBytes("test_chain_id") signStr := string(signBytes) expected := `{"chain_id":"test_chain_id","proposal":{"block_parts_header":{"hash":"626C6F636B7061727473","total":111},"height":12345,"pol_block_id":{},"pol_round":-1,"round":23456,"timestamp":"2018-02-11T07:09:22.765Z"}}` @@ -48,24 +48,25 @@ func TestProposalVerifySignature(t *testing.T) { pubKey := privVal.GetPubKey() prop := NewProposal(4, 2, PartSetHeader{777, []byte("proper")}, 2, BlockID{}) - signBytes := SignBytes("test_chain_id", prop) + signBytes := prop.SignBytes("test_chain_id") // sign it signature, err := privVal.Signer.Sign(signBytes) require.NoError(t, err) // verify the same proposal - valid := pubKey.VerifyBytes(SignBytes("test_chain_id", prop), signature) + valid := pubKey.VerifyBytes(prop.SignBytes("test_chain_id"), signature) require.True(t, valid) // serialize, deserialize and verify again.... newProp := new(Proposal) - bs := wire.BinaryBytes(prop) - err = wire.ReadBinaryBytes(bs, &newProp) + bs, err := wire.MarshalBinary(prop) + require.NoError(t, err) + err = wire.UnmarshalBinary(bs, &newProp) require.NoError(t, err) // verify the transmitted proposal - newSignBytes := SignBytes("test_chain_id", newProp) + newSignBytes := newProp.SignBytes("test_chain_id") require.Equal(t, string(signBytes), string(newSignBytes)) valid = pubKey.VerifyBytes(newSignBytes, signature) require.True(t, valid) @@ -73,14 +74,14 @@ func TestProposalVerifySignature(t *testing.T) { func BenchmarkProposalWriteSignBytes(b *testing.B) { for i := 0; i < b.N; i++ { - SignBytes("test_chain_id", testProposal) + testProposal.SignBytes("test_chain_id") } } func BenchmarkProposalSign(b *testing.B) { privVal := GenPrivValidatorFS("") for i := 0; i < b.N; i++ { - _, err := privVal.Signer.Sign(SignBytes("test_chain_id", testProposal)) + _, err := privVal.Signer.Sign(testProposal.SignBytes("test_chain_id")) if err != nil { b.Error(err) } @@ -88,12 +89,12 @@ func BenchmarkProposalSign(b *testing.B) { } func BenchmarkProposalVerifySignature(b *testing.B) { - signBytes := SignBytes("test_chain_id", testProposal) + signBytes := testProposal.SignBytes("test_chain_id") privVal := GenPrivValidatorFS("") signature, _ := privVal.Signer.Sign(signBytes) pubKey := privVal.GetPubKey() for i := 0; i < b.N; i++ { - pubKey.VerifyBytes(SignBytes("test_chain_id", testProposal), signature) + pubKey.VerifyBytes(testProposal.SignBytes("test_chain_id"), signature) } } diff --git a/types/results.go b/types/results.go index 4a02f2857..71834664d 100644 --- a/types/results.go +++ b/types/results.go @@ -2,7 +2,7 @@ package types import ( abci "github.com/tendermint/abci/types" - wire "github.com/tendermint/go-wire" + wire "github.com/tendermint/tendermint/wire" cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/merkle" ) @@ -18,7 +18,7 @@ type ABCIResult struct { // Hash returns the canonical hash of the ABCIResult func (a ABCIResult) Hash() []byte { - return wire.BinaryRipemd160(a) + return tmHash(a) } // ABCIResults wraps the deliver tx results to return a proof @@ -40,9 +40,13 @@ func NewResultFromResponse(response *abci.ResponseDeliverTx) ABCIResult { } } -// Bytes serializes the ABCIResponse using go-wire +// Bytes serializes the ABCIResponse using wire func (a ABCIResults) Bytes() []byte { - return wire.BinaryBytes(a) + bz, err := wire.MarshalBinary(a) + if err != nil { + panic(err) + } + return bz } // Hash returns a merkle hash of all results diff --git a/types/signable.go b/types/signable.go index bfdf9faa1..19829ede7 100644 --- a/types/signable.go +++ b/types/signable.go @@ -1,24 +1,16 @@ package types -import ( - "bytes" - "io" - - cmn "github.com/tendermint/tmlibs/common" -) - // Signable is an interface for all signable things. // It typically removes signatures before serializing. +// SignBytes returns the bytes to be signed +// NOTE: chainIDs are part of the SignBytes but not +// necessarily the object themselves. +// NOTE: Expected to panic if there is an error marshalling. type Signable interface { - WriteSignBytes(chainID string, w io.Writer, n *int, err *error) + SignBytes(chainID string) []byte } -// SignBytes is a convenience method for getting the bytes to sign of a Signable. -func SignBytes(chainID string, o Signable) []byte { - buf, n, err := new(bytes.Buffer), new(int), new(error) - o.WriteSignBytes(chainID, buf, n, err) - if *err != nil { - cmn.PanicCrisis(err) - } - return buf.Bytes() +// HashSignBytes is a convenience method for getting the hash of the bytes of a signable +func HashSignBytes(chainID string, o Signable) []byte { + return tmHash(o.SignBytes(chainID)) } diff --git a/types/test_util.go b/types/test_util.go index d13de04e2..73e53eb19 100644 --- a/types/test_util.go +++ b/types/test_util.go @@ -29,7 +29,7 @@ func MakeCommit(blockID BlockID, height int64, round int, } func signAddVote(privVal *PrivValidatorFS, vote *Vote, voteSet *VoteSet) (signed bool, err error) { - vote.Signature, err = privVal.Signer.Sign(SignBytes(voteSet.ChainID(), vote)) + vote.Signature, err = privVal.Signer.Sign(vote.SignBytes(voteSet.ChainID())) if err != nil { return false, err } diff --git a/types/tx.go b/types/tx.go index d9e2e2ebd..fc1d27212 100644 --- a/types/tx.go +++ b/types/tx.go @@ -11,12 +11,12 @@ import ( ) // Tx is an arbitrary byte array. -// NOTE: Tx has no types at this level, so when go-wire encoded it's just length-prefixed. +// NOTE: Tx has no types at this level, so when wire encoded it's just length-prefixed. // Alternatively, it may make sense to add types here and let // []byte be type 0x1 so we can have versioned txs if need be in the future. type Tx []byte -// Hash computes the RIPEMD160 hash of the go-wire encoded transaction. +// Hash computes the RIPEMD160 hash of the wire encoded transaction. func (tx Tx) Hash() []byte { return wireHasher(tx).Hash() } diff --git a/types/tx_test.go b/types/tx_test.go index dc99509dd..340bd5a6b 100644 --- a/types/tx_test.go +++ b/types/tx_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" - wire "github.com/tendermint/go-wire" + wire "github.com/tendermint/tendermint/wire" cmn "github.com/tendermint/tmlibs/common" ctest "github.com/tendermint/tmlibs/test" ) @@ -69,8 +69,9 @@ func TestValidTxProof(t *testing.T) { // read-write must also work var p2 TxProof - bin := wire.BinaryBytes(proof) - err := wire.ReadBinaryBytes(bin, &p2) + bin, err := wire.MarshalBinary(proof) + assert.Nil(err) + err = wire.UnmarshalBinary(bin, &p2) if assert.Nil(err, "%d: %d: %+v", h, i, err) { assert.Nil(p2.Validate(root), "%d: %d", h, i) } @@ -96,7 +97,8 @@ func testTxProofUnchangable(t *testing.T) { // make sure it is valid to start with assert.Nil(proof.Validate(root)) - bin := wire.BinaryBytes(proof) + bin, err := wire.MarshalBinary(proof) + assert.Nil(err) // try mutating the data and make sure nothing breaks for j := 0; j < 500; j++ { @@ -110,7 +112,7 @@ func testTxProofUnchangable(t *testing.T) { // this make sure the proof doesn't deserialize into something valid func assertBadProof(t *testing.T, root []byte, bad []byte, good TxProof) { var proof TxProof - err := wire.ReadBinaryBytes(bad, &proof) + err := wire.UnmarshalBinary(bad, &proof) if err == nil { err = proof.Validate(root) if err == nil { diff --git a/types/validator.go b/types/validator.go index dfe575515..027f7dc3c 100644 --- a/types/validator.go +++ b/types/validator.go @@ -3,10 +3,8 @@ package types import ( "bytes" "fmt" - "io" "github.com/tendermint/go-crypto" - "github.com/tendermint/go-wire" cmn "github.com/tendermint/tmlibs/common" ) @@ -14,9 +12,9 @@ import ( // NOTE: The Accum is not included in Validator.Hash(); // make sure to update that method if changes are made here type Validator struct { - Address Address `json:"address"` - PubKey crypto.PubKey `json:"pub_key"` - VotingPower int64 `json:"voting_power"` + Address Address `json:"address"` + PubKey crypto.PubKey `json:"pub_key"` + VotingPower int64 `json:"voting_power"` Accum int64 `json:"accum"` } @@ -72,7 +70,7 @@ func (v *Validator) String() string { // Hash computes the unique ID of a validator with a given voting power. // It excludes the Accum value, which changes with every round. func (v *Validator) Hash() []byte { - return wire.BinaryRipemd160(struct { + return tmHash(struct { Address Address PubKey crypto.PubKey VotingPower int64 @@ -83,25 +81,6 @@ func (v *Validator) Hash() []byte { }) } -//------------------------------------- - -var ValidatorCodec = validatorCodec{} - -type validatorCodec struct{} - -func (vc validatorCodec) Encode(o interface{}, w io.Writer, n *int, err *error) { - wire.WriteBinary(o.(*Validator), w, n, err) -} - -func (vc validatorCodec) Decode(r io.Reader, n *int, err *error) interface{} { - return wire.ReadBinary(&Validator{}, r, 0, n, err) -} - -func (vc validatorCodec) Compare(o1 interface{}, o2 interface{}) int { - cmn.PanicSanity("ValidatorCodec.Compare not implemented") - return 0 -} - //-------------------------------------------------------------------------------- // For testing... diff --git a/types/validator_set.go b/types/validator_set.go index 0d6c3249e..83d066ec1 100644 --- a/types/validator_set.go +++ b/types/validator_set.go @@ -253,7 +253,7 @@ func (valSet *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, height } _, val := valSet.GetByIndex(idx) // Validate signature - precommitSignBytes := SignBytes(chainID, precommit) + precommitSignBytes := precommit.SignBytes(chainID) if !val.PubKey.VerifyBytes(precommitSignBytes, precommit.Signature) { return fmt.Errorf("Invalid commit -- invalid signature: %v", precommit) } @@ -327,7 +327,7 @@ func (valSet *ValidatorSet) VerifyCommitAny(newSet *ValidatorSet, chainID string seen[vi] = true // Validate signature old school - precommitSignBytes := SignBytes(chainID, precommit) + precommitSignBytes := precommit.SignBytes(chainID) if !ov.PubKey.VerifyBytes(precommitSignBytes, precommit.Signature) { return errors.Errorf("Invalid commit -- invalid signature: %v", precommit) } diff --git a/types/validator_set_test.go b/types/validator_set_test.go index 9c7512378..b346be1be 100644 --- a/types/validator_set_test.go +++ b/types/validator_set_test.go @@ -6,33 +6,15 @@ import ( "strings" "testing" "testing/quick" + "time" "github.com/stretchr/testify/assert" + crypto "github.com/tendermint/go-crypto" - wire "github.com/tendermint/go-wire" + wire "github.com/tendermint/tendermint/wire" cmn "github.com/tendermint/tmlibs/common" ) -func randPubKey() crypto.PubKey { - var pubKey [32]byte - copy(pubKey[:], cmn.RandBytes(32)) - return crypto.PubKeyEd25519(pubKey).Wrap() -} - -func randValidator_() *Validator { - val := NewValidator(randPubKey(), cmn.RandInt64()) - val.Accum = cmn.RandInt64() - return val -} - -func randValidatorSet(numValidators int) *ValidatorSet { - validators := make([]*Validator, numValidators) - for i := 0; i < numValidators; i++ { - validators[i] = randValidator_() - } - return NewValidatorSet(validators) -} - func TestCopy(t *testing.T) { vset := randValidatorSet(10) vsetHash := vset.Hash() @@ -48,6 +30,26 @@ func TestCopy(t *testing.T) { } } +func BenchmarkValidatorSetCopy(b *testing.B) { + b.StopTimer() + vset := NewValidatorSet([]*Validator{}) + for i := 0; i < 1000; i++ { + privKey := crypto.GenPrivKeyEd25519() + pubKey := privKey.PubKey() + val := NewValidator(pubKey, 0) + if !vset.Add(val) { + panic("Failed to add validator") + } + } + b.StartTimer() + + for i := 0; i < b.N; i++ { + vset.Copy() + } +} + +//------------------------------------------------------------------- + func TestProposerSelection1(t *testing.T) { vset := NewValidatorSet([]*Validator{ newValidator([]byte("foo"), 1000), @@ -66,10 +68,6 @@ func TestProposerSelection1(t *testing.T) { } } -func newValidator(address []byte, power int64) *Validator { - return &Validator{Address: address, VotingPower: power} -} - func TestProposerSelection2(t *testing.T) { addr0 := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} addr1 := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} @@ -193,6 +191,48 @@ func TestProposerSelection3(t *testing.T) { } } +func newValidator(address []byte, power int64) *Validator { + return &Validator{Address: address, VotingPower: power} +} + +func randPubKey() crypto.PubKey { + var pubKey [32]byte + copy(pubKey[:], cmn.RandBytes(32)) + return crypto.PubKeyEd25519(pubKey).Wrap() +} + +func randValidator_() *Validator { + val := NewValidator(randPubKey(), cmn.RandInt64()) + val.Accum = cmn.RandInt64() + return val +} + +func randValidatorSet(numValidators int) *ValidatorSet { + validators := make([]*Validator, numValidators) + for i := 0; i < numValidators; i++ { + validators[i] = randValidator_() + } + return NewValidatorSet(validators) +} + +func (valSet *ValidatorSet) toBytes() []byte { + bz, err := wire.MarshalBinary(valSet) + if err != nil { + panic(err) + } + return bz +} + +func (valSet *ValidatorSet) fromBytes(b []byte) { + err := wire.UnmarshalBinary(b, &valSet) + if err != nil { + // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED + panic(err) + } +} + +//------------------------------------------------------------------- + func TestValidatorSetTotalVotingPowerOverflows(t *testing.T) { vset := NewValidatorSet([]*Validator{ {Address: []byte("a"), VotingPower: math.MaxInt64, Accum: 0}, @@ -272,38 +312,60 @@ func TestSafeSubClip(t *testing.T) { assert.EqualValues(t, math.MaxInt64, safeSubClip(math.MaxInt64, -10)) } -func BenchmarkValidatorSetCopy(b *testing.B) { - b.StopTimer() - vset := NewValidatorSet([]*Validator{}) - for i := 0; i < 1000; i++ { - privKey := crypto.GenPrivKeyEd25519() - pubKey := privKey.PubKey() - val := NewValidator(pubKey, 0) - if !vset.Add(val) { - panic("Failed to add validator") - } - } - b.StartTimer() +//------------------------------------------------------------------- - for i := 0; i < b.N; i++ { - vset.Copy() - } -} +func TestValidatorSetVerifyCommit(t *testing.T) { + privKey := crypto.GenPrivKeyEd25519() + pubKey := privKey.PubKey() + v1 := NewValidator(pubKey, 1000) + vset := NewValidatorSet([]*Validator{v1}) -func (valSet *ValidatorSet) toBytes() []byte { - buf, n, err := new(bytes.Buffer), new(int), new(error) - wire.WriteBinary(valSet, buf, n, err) - if *err != nil { - cmn.PanicCrisis(*err) + chainID := "mychainID" + blockID := BlockID{Hash: []byte("hello")} + height := int64(5) + vote := &Vote{ + ValidatorAddress: v1.Address, + ValidatorIndex: 0, + Height: height, + Round: 0, + Timestamp: time.Now().UTC(), + Type: VoteTypePrecommit, + BlockID: blockID, + } + vote.Signature = privKey.Sign(vote.SignBytes(chainID)) + commit := &Commit{ + BlockID: blockID, + Precommits: []*Vote{vote}, } - return buf.Bytes() -} -func (valSet *ValidatorSet) fromBytes(b []byte) { - r, n, err := bytes.NewReader(b), new(int), new(error) - wire.ReadBinary(valSet, r, 0, n, err) - if *err != nil { - // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED - cmn.PanicCrisis(*err) + badChainID := "notmychainID" + badBlockID := BlockID{Hash: []byte("goodbye")} + badHeight := height + 1 + badCommit := &Commit{ + BlockID: blockID, + Precommits: []*Vote{nil}, } + + // test some error cases + // TODO: test more cases! + cases := []struct { + chainID string + blockID BlockID + height int64 + commit *Commit + }{ + {badChainID, blockID, height, commit}, + {chainID, badBlockID, height, commit}, + {chainID, blockID, badHeight, commit}, + {chainID, blockID, height, badCommit}, + } + + for i, c := range cases { + err := vset.VerifyCommit(c.chainID, c.blockID, c.height, c.commit) + assert.NotNil(t, err, i) + } + + // test a good one + err := vset.VerifyCommit(chainID, blockID, height, commit) + assert.Nil(t, err) } diff --git a/types/vote.go b/types/vote.go index 7b069f2f6..6b36e0f4f 100644 --- a/types/vote.go +++ b/types/vote.go @@ -4,11 +4,10 @@ import ( "bytes" "errors" "fmt" - "io" "time" "github.com/tendermint/go-crypto" - "github.com/tendermint/go-wire" + "github.com/tendermint/tendermint/wire" cmn "github.com/tendermint/tmlibs/common" ) @@ -73,11 +72,15 @@ type Vote struct { Signature crypto.Signature `json:"signature"` } -func (vote *Vote) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) { - wire.WriteJSON(CanonicalJSONOnceVote{ +func (vote *Vote) SignBytes(chainID string) []byte { + bz, err := wire.MarshalJSON(CanonicalJSONOnceVote{ chainID, CanonicalVote(vote), - }, w, n, err) + }) + if err != nil { + panic(err) + } + return bz } func (vote *Vote) Copy() *Vote { @@ -111,7 +114,7 @@ func (vote *Vote) Verify(chainID string, pubKey crypto.PubKey) error { return ErrVoteInvalidValidatorAddress } - if !pubKey.VerifyBytes(SignBytes(chainID, vote), vote.Signature) { + if !pubKey.VerifyBytes(vote.SignBytes(chainID), vote.Signature) { return ErrVoteInvalidSignature } return nil diff --git a/types/vote_set.go b/types/vote_set.go index 2b5ac6316..37f26f4a5 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -8,10 +8,15 @@ import ( "github.com/pkg/errors" - "github.com/tendermint/tendermint/p2p" cmn "github.com/tendermint/tmlibs/common" ) +// UNSTABLE +// XXX: duplicate of p2p.ID to avoid dependence between packages. +// Perhaps we can have a minimal types package containing this (and other things?) +// that both `types` and `p2p` import ? +type P2PID string + /* VoteSet helps collect signatures from validators at each height+round for a predefined vote type. @@ -59,7 +64,7 @@ type VoteSet struct { sum int64 // Sum of voting power for seen votes, discounting conflicts maj23 *BlockID // First 2/3 majority seen votesByBlock map[string]*blockVotes // string(blockHash|blockParts) -> blockVotes - peerMaj23s map[p2p.ID]BlockID // Maj23 for each peer + peerMaj23s map[P2PID]BlockID // Maj23 for each peer } // Constructs a new VoteSet struct used to accumulate votes for given height/round. @@ -78,7 +83,7 @@ func NewVoteSet(chainID string, height int64, round int, type_ byte, valSet *Val sum: 0, maj23: nil, votesByBlock: make(map[string]*blockVotes, valSet.Size()), - peerMaj23s: make(map[p2p.ID]BlockID), + peerMaj23s: make(map[P2PID]BlockID), } } @@ -291,7 +296,7 @@ func (voteSet *VoteSet) addVerifiedVote(vote *Vote, blockKey string, votingPower // this can cause memory issues. // TODO: implement ability to remove peers too // NOTE: VoteSet must not be nil -func (voteSet *VoteSet) SetPeerMaj23(peerID p2p.ID, blockID BlockID) error { +func (voteSet *VoteSet) SetPeerMaj23(peerID P2PID, blockID BlockID) error { if voteSet == nil { cmn.PanicSanity("SetPeerMaj23() on nil VoteSet") } diff --git a/types/vote_test.go b/types/vote_test.go index 5e2d5c0df..a4a0f309f 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" - wire "github.com/tendermint/go-wire" + wire "github.com/tendermint/tendermint/wire" ) func examplePrevote() *Vote { @@ -42,7 +42,7 @@ func exampleVote(t byte) *Vote { func TestVoteSignable(t *testing.T) { vote := examplePrecommit() - signBytes := SignBytes("test_chain_id", vote) + signBytes := vote.SignBytes("test_chain_id") signStr := string(signBytes) expected := `{"chain_id":"test_chain_id","vote":{"block_id":{"hash":"68617368","parts":{"hash":"70617274735F68617368","total":1000000}},"height":12345,"round":2,"timestamp":"2017-12-25T03:00:01.234Z","type":2}}` @@ -77,24 +77,25 @@ func TestVoteVerifySignature(t *testing.T) { pubKey := privVal.GetPubKey() vote := examplePrecommit() - signBytes := SignBytes("test_chain_id", vote) + signBytes := vote.SignBytes("test_chain_id") // sign it signature, err := privVal.Signer.Sign(signBytes) require.NoError(t, err) // verify the same vote - valid := pubKey.VerifyBytes(SignBytes("test_chain_id", vote), signature) + valid := pubKey.VerifyBytes(vote.SignBytes("test_chain_id"), signature) require.True(t, valid) // serialize, deserialize and verify again.... precommit := new(Vote) - bs := wire.BinaryBytes(vote) - err = wire.ReadBinaryBytes(bs, &precommit) + bs, err := wire.MarshalBinary(vote) + require.NoError(t, err) + err = wire.UnmarshalBinary(bs, &precommit) require.NoError(t, err) // verify the transmitted vote - newSignBytes := SignBytes("test_chain_id", precommit) + newSignBytes := precommit.SignBytes("test_chain_id") require.Equal(t, string(signBytes), string(newSignBytes)) valid = pubKey.VerifyBytes(newSignBytes, signature) require.True(t, valid) diff --git a/wire/wire.go b/wire/wire.go new file mode 100644 index 000000000..9d0d2c208 --- /dev/null +++ b/wire/wire.go @@ -0,0 +1,60 @@ +package wire + +import ( + "github.com/tendermint/go-wire" +) + +/* +// Expose access to a global wire codec +// TODO: maybe introduce some Context object +// containing logger, config, codec that can +// be threaded through everything to avoid this global +var cdc *wire.Codec + +func init() { + cdc = wire.NewCodec() + crypto.RegisterWire(cdc) +} +*/ + +// Just a flow through to go-wire. +// To be used later for the global codec + +func MarshalBinary(o interface{}) ([]byte, error) { + return wire.MarshalBinary(o) +} + +func UnmarshalBinary(bz []byte, ptr interface{}) error { + return wire.UnmarshalBinary(bz, ptr) +} + +func MarshalJSON(o interface{}) ([]byte, error) { + return wire.MarshalJSON(o) +} + +func UnmarshalJSON(jsonBz []byte, ptr interface{}) error { + return wire.UnmarshalJSON(jsonBz, ptr) +} + +type ConcreteType = wire.ConcreteType + +func RegisterInterface(o interface{}, ctypes ...ConcreteType) *wire.TypeInfo { + return wire.RegisterInterface(o, ctypes...) +} + +const RFC3339Millis = wire.RFC3339Millis + +/* + +func RegisterInterface(ptr interface{}, opts *wire.InterfaceOptions) { + cdc.RegisterInterface(ptr, opts) +} + +func RegisterConcrete(o interface{}, name string, opts *wire.ConcreteOptions) { + cdc.RegisterConcrete(o, name, opts) +} + +//------------------------------- + +const RFC3339Millis = wire.RFC3339Millis +*/