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.

122 lines
3.1 KiB

  1. package p2p
  2. import (
  3. "bytes"
  4. "encoding/hex"
  5. "encoding/json"
  6. "fmt"
  7. "io/ioutil"
  8. "math/big"
  9. crypto "github.com/tendermint/go-crypto"
  10. cmn "github.com/tendermint/tmlibs/common"
  11. )
  12. type ID string
  13. //------------------------------------------------------------------------------
  14. // Persistent peer ID
  15. // TODO: encrypt on disk
  16. // NodeKey is the persistent peer key.
  17. // It contains the nodes private key for authentication.
  18. type NodeKey struct {
  19. PrivKey crypto.PrivKey `json:"priv_key"` // our priv key
  20. }
  21. // ID returns the peer's canonical ID - the hash of its public key.
  22. func (nodeKey *NodeKey) ID() ID {
  23. return ID(hex.EncodeToString(nodeKey.id()))
  24. }
  25. func (nodeKey *NodeKey) id() []byte {
  26. return nodeKey.PrivKey.PubKey().Address()
  27. }
  28. // PubKey returns the peer's PubKey
  29. func (nodeKey *NodeKey) PubKey() crypto.PubKey {
  30. return nodeKey.PrivKey.PubKey()
  31. }
  32. func (nodeKey *NodeKey) SatisfiesTarget(target []byte) bool {
  33. return bytes.Compare(nodeKey.id(), target) < 0
  34. }
  35. // LoadOrGenNodeKey attempts to load the NodeKey from the given filePath,
  36. // and checks that the corresponding ID is less than the target.
  37. // If the file does not exist, it generates and saves a new NodeKey
  38. // with ID less than target.
  39. func LoadOrGenNodeKey(filePath string, target []byte) (*NodeKey, error) {
  40. if cmn.FileExists(filePath) {
  41. nodeKey, err := loadNodeKey(filePath)
  42. if err != nil {
  43. return nil, err
  44. }
  45. if !nodeKey.SatisfiesTarget(target) {
  46. return nil, fmt.Errorf("Loaded ID (%s) does not satisfy target (%X)", nodeKey.ID(), target)
  47. }
  48. return nodeKey, nil
  49. } else {
  50. return genNodeKey(filePath, target)
  51. }
  52. }
  53. // MakePoWTarget returns a 20 byte target byte array.
  54. func MakePoWTarget(difficulty uint8) []byte {
  55. zeroPrefixLen := (int(difficulty) / 8)
  56. prefix := bytes.Repeat([]byte{0}, zeroPrefixLen)
  57. mod := (difficulty % 8)
  58. if mod > 0 {
  59. nonZeroPrefix := byte(1 << (8 - mod))
  60. prefix = append(prefix, nonZeroPrefix)
  61. }
  62. return append(prefix, bytes.Repeat([]byte{255}, 20-len(prefix))...)
  63. }
  64. func loadNodeKey(filePath string) (*NodeKey, error) {
  65. jsonBytes, err := ioutil.ReadFile(filePath)
  66. if err != nil {
  67. return nil, err
  68. }
  69. nodeKey := new(NodeKey)
  70. err = json.Unmarshal(jsonBytes, nodeKey)
  71. if err != nil {
  72. return nil, fmt.Errorf("Error reading NodeKey from %v: %v\n", filePath, err)
  73. }
  74. return nodeKey, nil
  75. }
  76. func genNodeKey(filePath string, target []byte) (*NodeKey, error) {
  77. privKey := genPrivKeyEd25519PoW(target).Wrap()
  78. nodeKey := &NodeKey{
  79. PrivKey: privKey,
  80. }
  81. jsonBytes, err := json.Marshal(nodeKey)
  82. if err != nil {
  83. return nil, err
  84. }
  85. err = ioutil.WriteFile(filePath, jsonBytes, 0600)
  86. if err != nil {
  87. return nil, err
  88. }
  89. return nodeKey, nil
  90. }
  91. // generate key with address satisfying the difficult target
  92. func genPrivKeyEd25519PoW(target []byte) crypto.PrivKeyEd25519 {
  93. secret := crypto.CRandBytes(32)
  94. var privKey crypto.PrivKeyEd25519
  95. for i := 0; ; i++ {
  96. privKey = crypto.GenPrivKeyEd25519FromSecret(secret)
  97. if bytes.Compare(privKey.PubKey().Address(), target) < 0 {
  98. break
  99. }
  100. z := new(big.Int)
  101. z.SetBytes(secret)
  102. z = z.Add(z, big.NewInt(1))
  103. secret = z.Bytes()
  104. }
  105. return privKey
  106. }