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 }