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.

120 lines
3.2 KiB

  1. package p2p
  2. import (
  3. "bytes"
  4. "encoding/hex"
  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. // ID is a hex-encoded crypto.Address
  13. type ID string
  14. // IDByteLength is the length of a crypto.Address. Currently only 20.
  15. // TODO: support other length addresses ?
  16. const IDByteLength = crypto.AddressSize
  17. //------------------------------------------------------------------------------
  18. // Persistent peer ID
  19. // TODO: encrypt on disk
  20. // NodeKey is the persistent peer key.
  21. // It contains the nodes private key for authentication.
  22. type NodeKey struct {
  23. PrivKey crypto.PrivKey `json:"priv_key"` // our priv key
  24. }
  25. // ID returns the peer's canonical ID - the hash of its public key.
  26. func (nodeKey *NodeKey) ID() ID {
  27. return PubKeyToID(nodeKey.PubKey())
  28. }
  29. // PubKey returns the peer's PubKey
  30. func (nodeKey *NodeKey) PubKey() crypto.PubKey {
  31. return nodeKey.PrivKey.PubKey()
  32. }
  33. // PubKeyToID returns the ID corresponding to the given PubKey.
  34. // It's the hex-encoding of the pubKey.Address().
  35. func PubKeyToID(pubKey crypto.PubKey) ID {
  36. return ID(hex.EncodeToString(pubKey.Address()))
  37. }
  38. // LoadOrGenNodeKey attempts to load the NodeKey from the given filePath. If
  39. // the file does not exist, it generates and saves a new NodeKey.
  40. func LoadOrGenNodeKey(filePath string) (*NodeKey, error) {
  41. if tmos.FileExists(filePath) {
  42. nodeKey, err := LoadNodeKey(filePath)
  43. if err != nil {
  44. return nil, err
  45. }
  46. return nodeKey, nil
  47. }
  48. privKey := ed25519.GenPrivKey()
  49. nodeKey := &NodeKey{
  50. PrivKey: privKey,
  51. }
  52. if err := nodeKey.SaveAs(filePath); err != nil {
  53. return nil, err
  54. }
  55. return nodeKey, nil
  56. }
  57. // LoadNodeKey loads NodeKey located in filePath.
  58. func LoadNodeKey(filePath string) (*NodeKey, error) {
  59. jsonBytes, err := ioutil.ReadFile(filePath)
  60. if err != nil {
  61. return nil, err
  62. }
  63. nodeKey := new(NodeKey)
  64. err = tmjson.Unmarshal(jsonBytes, nodeKey)
  65. if err != nil {
  66. return nil, err
  67. }
  68. return nodeKey, nil
  69. }
  70. // SaveAs persists the NodeKey to filePath.
  71. func (nodeKey *NodeKey) SaveAs(filePath string) error {
  72. jsonBytes, err := tmjson.Marshal(nodeKey)
  73. if err != nil {
  74. return err
  75. }
  76. err = ioutil.WriteFile(filePath, jsonBytes, 0600)
  77. if err != nil {
  78. return err
  79. }
  80. return nil
  81. }
  82. //------------------------------------------------------------------------------
  83. // MakePoWTarget returns the big-endian encoding of 2^(targetBits - difficulty) - 1.
  84. // It can be used as a Proof of Work target.
  85. // NOTE: targetBits must be a multiple of 8 and difficulty must be less than targetBits.
  86. func MakePoWTarget(difficulty, targetBits uint) []byte {
  87. if targetBits%8 != 0 {
  88. panic(fmt.Sprintf("targetBits (%d) not a multiple of 8", targetBits))
  89. }
  90. if difficulty >= targetBits {
  91. panic(fmt.Sprintf("difficulty (%d) >= targetBits (%d)", difficulty, targetBits))
  92. }
  93. targetBytes := targetBits / 8
  94. zeroPrefixLen := (int(difficulty) / 8)
  95. prefix := bytes.Repeat([]byte{0}, zeroPrefixLen)
  96. mod := (difficulty % 8)
  97. if mod > 0 {
  98. nonZeroPrefix := byte(1<<(8-mod) - 1)
  99. prefix = append(prefix, nonZeroPrefix)
  100. }
  101. tailLen := int(targetBytes) - len(prefix)
  102. return append(prefix, bytes.Repeat([]byte{0xFF}, tailLen)...)
  103. }