You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

127 lines
3.2 KiB

  1. package p2p
  2. import (
  3. "encoding/hex"
  4. "errors"
  5. "fmt"
  6. "io/ioutil"
  7. "github.com/tendermint/tendermint/crypto"
  8. "github.com/tendermint/tendermint/crypto/ed25519"
  9. tmjson "github.com/tendermint/tendermint/libs/json"
  10. tmos "github.com/tendermint/tendermint/libs/os"
  11. )
  12. // NodeID is a hex-encoded crypto.Address.
  13. // FIXME: We should either ensure this is always lowercased, or add an Equal()
  14. // for comparison that decodes to the binary byte slice first.
  15. type NodeID string
  16. // NodeIDByteLength is the length of a crypto.Address. Currently only 20.
  17. // FIXME: support other length addresses?
  18. const NodeIDByteLength = crypto.AddressSize
  19. // NodeIDFromPubKey returns the noe ID corresponding to the given PubKey. It's
  20. // the hex-encoding of the pubKey.Address().
  21. func NodeIDFromPubKey(pubKey crypto.PubKey) NodeID {
  22. return NodeID(hex.EncodeToString(pubKey.Address()))
  23. }
  24. // Bytes converts the node ID to it's binary byte representation.
  25. func (id NodeID) Bytes() ([]byte, error) {
  26. bz, err := hex.DecodeString(string(id))
  27. if err != nil {
  28. return nil, fmt.Errorf("invalid node ID encoding: %w", err)
  29. }
  30. return bz, nil
  31. }
  32. // Validate validates the NodeID.
  33. func (id NodeID) Validate() error {
  34. if len(id) == 0 {
  35. return errors.New("no ID")
  36. }
  37. bz, err := id.Bytes()
  38. if err != nil {
  39. return err
  40. }
  41. if len(bz) != NodeIDByteLength {
  42. return fmt.Errorf("invalid ID length - got %d, expected %d", len(bz), NodeIDByteLength)
  43. }
  44. return nil
  45. }
  46. //------------------------------------------------------------------------------
  47. // Persistent peer ID
  48. // TODO: encrypt on disk
  49. // NodeKey is the persistent peer key.
  50. // It contains the nodes private key for authentication.
  51. type NodeKey struct {
  52. // Canonical ID - hex-encoded pubkey's address (IDByteLength bytes)
  53. ID NodeID `json:"id"`
  54. // Private key
  55. PrivKey crypto.PrivKey `json:"priv_key"`
  56. }
  57. // PubKey returns the peer's PubKey
  58. func (nodeKey NodeKey) PubKey() crypto.PubKey {
  59. return nodeKey.PrivKey.PubKey()
  60. }
  61. // SaveAs persists the NodeKey to filePath.
  62. func (nodeKey NodeKey) SaveAs(filePath string) error {
  63. jsonBytes, err := tmjson.Marshal(nodeKey)
  64. if err != nil {
  65. return err
  66. }
  67. err = ioutil.WriteFile(filePath, jsonBytes, 0600)
  68. if err != nil {
  69. return err
  70. }
  71. return nil
  72. }
  73. // LoadOrGenNodeKey attempts to load the NodeKey from the given filePath. If
  74. // the file does not exist, it generates and saves a new NodeKey.
  75. func LoadOrGenNodeKey(filePath string) (NodeKey, error) {
  76. if tmos.FileExists(filePath) {
  77. nodeKey, err := LoadNodeKey(filePath)
  78. if err != nil {
  79. return NodeKey{}, err
  80. }
  81. return nodeKey, nil
  82. }
  83. nodeKey := GenNodeKey()
  84. if err := nodeKey.SaveAs(filePath); err != nil {
  85. return NodeKey{}, err
  86. }
  87. return nodeKey, nil
  88. }
  89. // GenNodeKey generates a new node key.
  90. func GenNodeKey() NodeKey {
  91. privKey := ed25519.GenPrivKey()
  92. return NodeKey{
  93. ID: NodeIDFromPubKey(privKey.PubKey()),
  94. PrivKey: privKey,
  95. }
  96. }
  97. // LoadNodeKey loads NodeKey located in filePath.
  98. func LoadNodeKey(filePath string) (NodeKey, error) {
  99. jsonBytes, err := ioutil.ReadFile(filePath)
  100. if err != nil {
  101. return NodeKey{}, err
  102. }
  103. nodeKey := NodeKey{}
  104. err = tmjson.Unmarshal(jsonBytes, &nodeKey)
  105. if err != nil {
  106. return NodeKey{}, err
  107. }
  108. nodeKey.ID = NodeIDFromPubKey(nodeKey.PubKey())
  109. return nodeKey, nil
  110. }