![Ethan Buchman](/avatar/91dd51f38be716f8c29da7f4f656bc16) p2p: introduce peerConn to simplify peer creation (#1226)
* expose AuthEnc in the P2P config
if AuthEnc is true, dialed peers must have a node ID in the address and
it must match the persistent pubkey from the secret handshake.
Refs #1157
* fixes after my own review
* fix docs
* fix build failure
```
p2p/pex/pex_reactor_test.go:288:88: cannot use seed.NodeInfo().NetAddress() (type *p2p.NetAddress) as type string in array or slice literal
```
* p2p: introduce peerConn to simplify peer creation
* Introduce `peerConn` containing the known fields of `peer`
* `peer` only created in `sw.addPeer` once handshake is complete and NodeInfo is checked
* Eliminates some mutable variables and makes the code flow better
* Simplifies the `newXxxPeer` funcs
* Use ID instead of PubKey where possible.
* SetPubKeyFilter -> SetIDFilter
* nodeInfo.Validate takes ID
* remove peer.PubKey()
* persistent node ids
* fixes from review
* test: use ip_plus_id.sh more
* fix invalid memory panic during fast_sync test
```
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: panic: runtime error: invalid memory address or nil pointer dereference
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: [signal SIGSEGV: segmentation violation code=0x1 addr=0x20 pc=0x98dd3e]
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]:
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: goroutine 3432 [running]:
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: github.com/tendermint/tendermint/p2p.newOutboundPeerConn(0xc423fd1380, 0xc420933e00, 0x1, 0x1239a60, 0
xc420128c40, 0x2, 0x42caf6, 0xc42001f300, 0xc422831d98, 0xc4227951c0, ...)
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: #011/go/src/github.com/tendermint/tendermint/p2p/peer.go:123 +0x31e
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: github.com/tendermint/tendermint/p2p.(*Switch).addOutboundPeerWithConfig(0xc4200ad040, 0xc423fd1380, 0
xc420933e00, 0xc423f48801, 0x28, 0x2)
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: #011/go/src/github.com/tendermint/tendermint/p2p/switch.go:455 +0x12b
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: github.com/tendermint/tendermint/p2p.(*Switch).DialPeerWithAddress(0xc4200ad040, 0xc423fd1380, 0x1, 0x
0, 0x0)
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: #011/go/src/github.com/tendermint/tendermint/p2p/switch.go:371 +0xdc
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: github.com/tendermint/tendermint/p2p.(*Switch).reconnectToPeer(0xc4200ad040, 0x123e000, 0xc42007bb00)
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: #011/go/src/github.com/tendermint/tendermint/p2p/switch.go:290 +0x25f
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: created by github.com/tendermint/tendermint/p2p.(*Switch).StopPeerForError
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: #011/go/src/github.com/tendermint/tendermint/p2p/switch.go:256 +0x1b7
```
7 years ago |
|
- package types
-
- import (
- "bytes"
- "encoding/json"
- "errors"
- "fmt"
- "io/ioutil"
- "sync"
- "time"
-
- crypto "github.com/tendermint/go-crypto"
- cmn "github.com/tendermint/tmlibs/common"
- )
-
- // TODO: type ?
- const (
- stepNone int8 = 0 // Used to distinguish the initial state
- stepPropose int8 = 1
- stepPrevote int8 = 2
- stepPrecommit int8 = 3
- )
-
- func voteToStep(vote *Vote) int8 {
- switch vote.Type {
- case VoteTypePrevote:
- return stepPrevote
- case VoteTypePrecommit:
- return stepPrecommit
- default:
- cmn.PanicSanity("Unknown vote type")
- return 0
- }
- }
-
- //--------------------------------------------------------------
- // PrivValidator is being upgraded! See types/priv_validator/
-
- // ValidatorID contains the identity of the validator.
- type ValidatorID struct {
- Address cmn.HexBytes `json:"address"`
- PubKey crypto.PubKey `json:"pub_key"`
- }
-
- // PrivValidator defines the functionality of a local Tendermint validator
- // that signs votes, proposals, and heartbeats, and never double signs.
- type PrivValidator2 interface {
- Address() (Address, error) // redundant since .PubKey().Address()
- PubKey() (crypto.PubKey, error)
-
- SignVote(chainID string, vote *Vote) error
- SignProposal(chainID string, proposal *Proposal) error
- SignHeartbeat(chainID string, heartbeat *Heartbeat) error
- }
-
- type TestSigner interface {
- Address() cmn.HexBytes
- PubKey() crypto.PubKey
- Sign([]byte) (crypto.Signature, error)
- }
-
- func GenSigner() TestSigner {
- return &DefaultTestSigner{
- crypto.GenPrivKeyEd25519().Wrap(),
- }
- }
-
- type DefaultTestSigner struct {
- crypto.PrivKey
- }
-
- func (ds *DefaultTestSigner) Address() cmn.HexBytes {
- return ds.PubKey().Address()
- }
-
- func (ds *DefaultTestSigner) PubKey() crypto.PubKey {
- return ds.PrivKey.PubKey()
- }
-
- func (ds *DefaultTestSigner) Sign(msg []byte) (crypto.Signature, error) {
- return ds.PrivKey.Sign(msg), nil
- }
-
- //--------------------------------------------------------------
- // TODO: Deprecate!
-
- // PrivValidator defines the functionality of a local Tendermint validator
- // that signs votes, proposals, and heartbeats, and never double signs.
- type PrivValidator interface {
- GetAddress() Address // redundant since .PubKey().Address()
- GetPubKey() crypto.PubKey
-
- SignVote(chainID string, vote *Vote) error
- SignProposal(chainID string, proposal *Proposal) error
- SignHeartbeat(chainID string, heartbeat *Heartbeat) error
- }
-
- // PrivValidatorFS implements PrivValidator using data persisted to disk
- // to prevent double signing. The Signer itself can be mutated to use
- // something besides the default, for instance a hardware signer.
- type PrivValidatorFS struct {
- Address Address `json:"address"`
- PubKey crypto.PubKey `json:"pub_key"`
- LastHeight int64 `json:"last_height"`
- LastRound int `json:"last_round"`
- LastStep int8 `json:"last_step"`
- LastSignature crypto.Signature `json:"last_signature,omitempty"` // so we dont lose signatures
- LastSignBytes cmn.HexBytes `json:"last_signbytes,omitempty"` // so we dont lose signatures
-
- // PrivKey should be empty if a Signer other than the default is being used.
- PrivKey crypto.PrivKey `json:"priv_key"`
- Signer `json:"-"`
-
- // For persistence.
- // Overloaded for testing.
- filePath string
- mtx sync.Mutex
- }
-
- // Signer is an interface that defines how to sign messages.
- // It is the caller's duty to verify the msg before calling Sign,
- // eg. to avoid double signing.
- // Currently, the only callers are SignVote, SignProposal, and SignHeartbeat.
- type Signer interface {
- Sign(msg []byte) (crypto.Signature, error)
- }
-
- // DefaultSigner implements Signer.
- // It uses a standard, unencrypted crypto.PrivKey.
- type DefaultSigner struct {
- PrivKey crypto.PrivKey `json:"priv_key"`
- }
-
- // NewDefaultSigner returns an instance of DefaultSigner.
- func NewDefaultSigner(priv crypto.PrivKey) *DefaultSigner {
- return &DefaultSigner{
- PrivKey: priv,
- }
- }
-
- // Sign implements Signer. It signs the byte slice with a private key.
- func (ds *DefaultSigner) Sign(msg []byte) (crypto.Signature, error) {
- return ds.PrivKey.Sign(msg), nil
- }
-
- // GetAddress returns the address of the validator.
- // Implements PrivValidator.
- func (pv *PrivValidatorFS) GetAddress() Address {
- return pv.Address
- }
-
- // GetPubKey returns the public key of the validator.
- // Implements PrivValidator.
- func (pv *PrivValidatorFS) GetPubKey() crypto.PubKey {
- return pv.PubKey
- }
-
- // GenPrivValidatorFS generates a new validator with randomly generated private key
- // and sets the filePath, but does not call Save().
- func GenPrivValidatorFS(filePath string) *PrivValidatorFS {
- privKey := crypto.GenPrivKeyEd25519().Wrap()
- return &PrivValidatorFS{
- Address: privKey.PubKey().Address(),
- PubKey: privKey.PubKey(),
- PrivKey: privKey,
- LastStep: stepNone,
- Signer: NewDefaultSigner(privKey),
- filePath: filePath,
- }
- }
-
- // LoadPrivValidatorFS loads a PrivValidatorFS from the filePath.
- func LoadPrivValidatorFS(filePath string) *PrivValidatorFS {
- return LoadPrivValidatorFSWithSigner(filePath, func(privVal PrivValidator) Signer {
- return NewDefaultSigner(privVal.(*PrivValidatorFS).PrivKey)
- })
- }
-
- // LoadOrGenPrivValidatorFS loads a PrivValidatorFS from the given filePath
- // or else generates a new one and saves it to the filePath.
- func LoadOrGenPrivValidatorFS(filePath string) *PrivValidatorFS {
- var privVal *PrivValidatorFS
- if cmn.FileExists(filePath) {
- privVal = LoadPrivValidatorFS(filePath)
- } else {
- privVal = GenPrivValidatorFS(filePath)
- privVal.Save()
- }
- return privVal
- }
-
- // LoadPrivValidatorWithSigner loads a PrivValidatorFS with a custom
- // signer object. The PrivValidatorFS handles double signing prevention by persisting
- // data to the filePath, while the Signer handles the signing.
- // If the filePath does not exist, the PrivValidatorFS must be created manually and saved.
- func LoadPrivValidatorFSWithSigner(filePath string, signerFunc func(PrivValidator) Signer) *PrivValidatorFS {
- privValJSONBytes, err := ioutil.ReadFile(filePath)
- if err != nil {
- cmn.Exit(err.Error())
- }
- privVal := &PrivValidatorFS{}
- err = json.Unmarshal(privValJSONBytes, &privVal)
- if err != nil {
- cmn.Exit(cmn.Fmt("Error reading PrivValidator from %v: %v\n", filePath, err))
- }
-
- privVal.filePath = filePath
- privVal.Signer = signerFunc(privVal)
- return privVal
- }
-
- // Save persists the PrivValidatorFS to disk.
- func (privVal *PrivValidatorFS) Save() {
- privVal.mtx.Lock()
- defer privVal.mtx.Unlock()
- privVal.save()
- }
-
- func (privVal *PrivValidatorFS) save() {
- if privVal.filePath == "" {
- cmn.PanicSanity("Cannot save PrivValidator: filePath not set")
- }
- jsonBytes, err := json.Marshal(privVal)
- if err != nil {
- // `@; BOOM!!!
- cmn.PanicCrisis(err)
- }
- err = cmn.WriteFileAtomic(privVal.filePath, jsonBytes, 0600)
- if err != nil {
- // `@; BOOM!!!
- cmn.PanicCrisis(err)
- }
- }
-
- // 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 = sig
- privVal.LastSignBytes = nil
- privVal.Save()
- }
-
- // SignVote signs a canonical representation of the vote, along with the
- // chainID. Implements PrivValidator.
- func (privVal *PrivValidatorFS) SignVote(chainID string, vote *Vote) error {
- privVal.mtx.Lock()
- defer privVal.mtx.Unlock()
- if err := privVal.signVote(chainID, vote); err != nil {
- return errors.New(cmn.Fmt("Error signing vote: %v", err))
- }
- return nil
- }
-
- // SignProposal signs a canonical representation of the proposal, along with
- // the chainID. Implements PrivValidator.
- func (privVal *PrivValidatorFS) SignProposal(chainID string, proposal *Proposal) error {
- privVal.mtx.Lock()
- defer privVal.mtx.Unlock()
- if err := privVal.signProposal(chainID, proposal); err != nil {
- return fmt.Errorf("Error signing proposal: %v", err)
- }
- return nil
- }
-
- // returns error if HRS regression or no LastSignBytes. returns true if HRS is unchanged
- func (privVal *PrivValidatorFS) checkHRS(height int64, round int, step int8) (bool, error) {
- if privVal.LastHeight > height {
- return false, errors.New("Height regression")
- }
-
- if privVal.LastHeight == height {
- if privVal.LastRound > round {
- return false, errors.New("Round regression")
- }
-
- if privVal.LastRound == round {
- if privVal.LastStep > step {
- return false, errors.New("Step regression")
- } else if privVal.LastStep == step {
- if privVal.LastSignBytes != nil {
- if privVal.LastSignature.Empty() {
- panic("privVal: LastSignature is nil but LastSignBytes is not!")
- }
- return true, nil
- }
- return false, errors.New("No LastSignature found")
- }
- }
- }
- return false, nil
- }
-
- // signVote checks if the vote is good to sign and sets the vote signature.
- // It may need to set the timestamp as well if the vote is otherwise the same as
- // 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 := vote.SignBytes(chainID)
-
- sameHRS, err := privVal.checkHRS(height, round, step)
- if err != nil {
- return err
- }
-
- // We might crash before writing to the wal,
- // causing us to try to re-sign for the same HRS.
- // If signbytes are the same, use the last signature.
- // If they only differ by timestamp, use last timestamp and signature
- // Otherwise, return error
- if sameHRS {
- if bytes.Equal(signBytes, privVal.LastSignBytes) {
- vote.Signature = privVal.LastSignature
- } else if timestamp, ok := checkVotesOnlyDifferByTimestamp(privVal.LastSignBytes, signBytes); ok {
- vote.Timestamp = timestamp
- vote.Signature = privVal.LastSignature
- } else {
- err = fmt.Errorf("Conflicting data")
- }
- return err
- }
-
- // It passed the checks. Sign the vote
- sig, err := privVal.Sign(signBytes)
- if err != nil {
- return err
- }
- privVal.saveSigned(height, round, step, signBytes, sig)
- vote.Signature = sig
- return nil
- }
-
- // signProposal checks if the proposal is good to sign and sets the proposal signature.
- // It may need to set the timestamp as well if the proposal is otherwise the same as
- // 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 := proposal.SignBytes(chainID)
-
- sameHRS, err := privVal.checkHRS(height, round, step)
- if err != nil {
- return err
- }
-
- // We might crash before writing to the wal,
- // causing us to try to re-sign for the same HRS.
- // If signbytes are the same, use the last signature.
- // If they only differ by timestamp, use last timestamp and signature
- // Otherwise, return error
- if sameHRS {
- if bytes.Equal(signBytes, privVal.LastSignBytes) {
- proposal.Signature = privVal.LastSignature
- } else if timestamp, ok := checkProposalsOnlyDifferByTimestamp(privVal.LastSignBytes, signBytes); ok {
- proposal.Timestamp = timestamp
- proposal.Signature = privVal.LastSignature
- } else {
- err = fmt.Errorf("Conflicting data")
- }
- return err
- }
-
- // It passed the checks. Sign the proposal
- sig, err := privVal.Sign(signBytes)
- if err != nil {
- return err
- }
- privVal.saveSigned(height, round, step, signBytes, sig)
- proposal.Signature = sig
- return nil
- }
-
- // Persist height/round/step and signature
- func (privVal *PrivValidatorFS) saveSigned(height int64, round int, step int8,
- signBytes []byte, sig crypto.Signature) {
-
- privVal.LastHeight = height
- privVal.LastRound = round
- privVal.LastStep = step
- privVal.LastSignature = sig
- privVal.LastSignBytes = signBytes
- privVal.save()
- }
-
- // SignHeartbeat signs a canonical representation of the heartbeat, along with the chainID.
- // Implements PrivValidator.
- func (privVal *PrivValidatorFS) SignHeartbeat(chainID string, heartbeat *Heartbeat) error {
- privVal.mtx.Lock()
- defer privVal.mtx.Unlock()
- var err error
- heartbeat.Signature, err = privVal.Sign(heartbeat.SignBytes(chainID))
- return err
- }
-
- // String returns a string representation of the PrivValidatorFS.
- func (privVal *PrivValidatorFS) String() string {
- return fmt.Sprintf("PrivValidator{%v LH:%v, LR:%v, LS:%v}", privVal.GetAddress(), privVal.LastHeight, privVal.LastRound, privVal.LastStep)
- }
-
- //-------------------------------------
-
- type PrivValidatorsByAddress []*PrivValidatorFS
-
- func (pvs PrivValidatorsByAddress) Len() int {
- return len(pvs)
- }
-
- func (pvs PrivValidatorsByAddress) Less(i, j int) bool {
- return bytes.Compare(pvs[i].GetAddress(), pvs[j].GetAddress()) == -1
- }
-
- func (pvs PrivValidatorsByAddress) Swap(i, j int) {
- it := pvs[i]
- pvs[i] = pvs[j]
- pvs[j] = it
- }
-
- //-------------------------------------
-
- // returns the timestamp from the lastSignBytes.
- // returns true if the only difference in the votes is their timestamp.
- func checkVotesOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.Time, bool) {
- var lastVote, newVote CanonicalJSONOnceVote
- if err := json.Unmarshal(lastSignBytes, &lastVote); err != nil {
- panic(fmt.Sprintf("LastSignBytes cannot be unmarshalled into vote: %v", err))
- }
- if err := json.Unmarshal(newSignBytes, &newVote); err != nil {
- panic(fmt.Sprintf("signBytes cannot be unmarshalled into vote: %v", err))
- }
-
- lastTime, err := time.Parse(TimeFormat, lastVote.Vote.Timestamp)
- if err != nil {
- panic(err)
- }
-
- // set the times to the same value and check equality
- now := CanonicalTime(time.Now())
- lastVote.Vote.Timestamp = now
- newVote.Vote.Timestamp = now
- lastVoteBytes, _ := json.Marshal(lastVote)
- newVoteBytes, _ := json.Marshal(newVote)
-
- return lastTime, bytes.Equal(newVoteBytes, lastVoteBytes)
- }
-
- // returns the timestamp from the lastSignBytes.
- // returns true if the only difference in the proposals is their timestamp
- func checkProposalsOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.Time, bool) {
- var lastProposal, newProposal CanonicalJSONOnceProposal
- if err := json.Unmarshal(lastSignBytes, &lastProposal); err != nil {
- panic(fmt.Sprintf("LastSignBytes cannot be unmarshalled into proposal: %v", err))
- }
- if err := json.Unmarshal(newSignBytes, &newProposal); err != nil {
- panic(fmt.Sprintf("signBytes cannot be unmarshalled into proposal: %v", err))
- }
-
- lastTime, err := time.Parse(TimeFormat, lastProposal.Proposal.Timestamp)
- if err != nil {
- panic(err)
- }
-
- // set the times to the same value and check equality
- now := CanonicalTime(time.Now())
- lastProposal.Proposal.Timestamp = now
- newProposal.Proposal.Timestamp = now
- lastProposalBytes, _ := json.Marshal(lastProposal)
- newProposalBytes, _ := json.Marshal(newProposal)
-
- return lastTime, bytes.Equal(newProposalBytes, lastProposalBytes)
- }
|