package p2p
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"math/big"
|
|
|
|
crypto "github.com/tendermint/go-crypto"
|
|
cmn "github.com/tendermint/tmlibs/common"
|
|
)
|
|
|
|
type ID string
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Persistent peer ID
|
|
// TODO: encrypt on disk
|
|
|
|
// NodeKey is the persistent peer key.
|
|
// It contains the nodes private key for authentication.
|
|
type NodeKey struct {
|
|
PrivKey crypto.PrivKey `json:"priv_key"` // our priv key
|
|
}
|
|
|
|
// ID returns the peer's canonical ID - the hash of its public key.
|
|
func (nodeKey *NodeKey) ID() ID {
|
|
return ID(hex.EncodeToString(nodeKey.id()))
|
|
}
|
|
|
|
func (nodeKey *NodeKey) id() []byte {
|
|
return nodeKey.PrivKey.PubKey().Address()
|
|
}
|
|
|
|
// PubKey returns the peer's PubKey
|
|
func (nodeKey *NodeKey) PubKey() crypto.PubKey {
|
|
return nodeKey.PrivKey.PubKey()
|
|
}
|
|
|
|
func (nodeKey *NodeKey) SatisfiesTarget(target []byte) bool {
|
|
return bytes.Compare(nodeKey.id(), target) < 0
|
|
}
|
|
|
|
// LoadOrGenNodeKey attempts to load the NodeKey from the given filePath,
|
|
// and checks that the corresponding ID is less than the target.
|
|
// If the file does not exist, it generates and saves a new NodeKey
|
|
// with ID less than target.
|
|
func LoadOrGenNodeKey(filePath string, target []byte) (*NodeKey, error) {
|
|
if cmn.FileExists(filePath) {
|
|
nodeKey, err := loadNodeKey(filePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !nodeKey.SatisfiesTarget(target) {
|
|
return nil, fmt.Errorf("Loaded ID (%s) does not satisfy target (%X)", nodeKey.ID(), target)
|
|
}
|
|
return nodeKey, nil
|
|
} else {
|
|
return genNodeKey(filePath, target)
|
|
}
|
|
}
|
|
|
|
// MakePoWTarget returns a 20 byte target byte array.
|
|
func MakePoWTarget(difficulty uint8) []byte {
|
|
zeroPrefixLen := (int(difficulty) / 8)
|
|
prefix := bytes.Repeat([]byte{0}, zeroPrefixLen)
|
|
mod := (difficulty % 8)
|
|
if mod > 0 {
|
|
nonZeroPrefix := byte(1 << (8 - mod))
|
|
prefix = append(prefix, nonZeroPrefix)
|
|
}
|
|
return append(prefix, bytes.Repeat([]byte{255}, 20-len(prefix))...)
|
|
}
|
|
|
|
func loadNodeKey(filePath string) (*NodeKey, error) {
|
|
jsonBytes, err := ioutil.ReadFile(filePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
nodeKey := new(NodeKey)
|
|
err = json.Unmarshal(jsonBytes, nodeKey)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Error reading NodeKey from %v: %v\n", filePath, err)
|
|
}
|
|
return nodeKey, nil
|
|
}
|
|
|
|
func genNodeKey(filePath string, target []byte) (*NodeKey, error) {
|
|
privKey := genPrivKeyEd25519PoW(target).Wrap()
|
|
nodeKey := &NodeKey{
|
|
PrivKey: privKey,
|
|
}
|
|
|
|
jsonBytes, err := json.Marshal(nodeKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = ioutil.WriteFile(filePath, jsonBytes, 0600)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return nodeKey, nil
|
|
}
|
|
|
|
// generate key with address satisfying the difficult target
|
|
func genPrivKeyEd25519PoW(target []byte) crypto.PrivKeyEd25519 {
|
|
secret := crypto.CRandBytes(32)
|
|
var privKey crypto.PrivKeyEd25519
|
|
for i := 0; ; i++ {
|
|
privKey = crypto.GenPrivKeyEd25519FromSecret(secret)
|
|
if bytes.Compare(privKey.PubKey().Address(), target) < 0 {
|
|
break
|
|
}
|
|
z := new(big.Int)
|
|
z.SetBytes(secret)
|
|
z = z.Add(z, big.NewInt(1))
|
|
secret = z.Bytes()
|
|
|
|
}
|
|
return privKey
|
|
}
|