package utils
import (
"bufio"
"log"
"os"
"strconv"
"strings"
"github.com/harmony-one/harmony/crypto"
"github.com/harmony-one/harmony/crypto/pki"
"github.com/harmony-one/harmony/p2p"
)
// ConfigEntry is the config entry.
type ConfigEntry struct {
IP string
Port string
Role string
ShardID string
ValidatorID int // Validator ID in its shard.
}
// DistributionConfig is the distribution config.
type DistributionConfig struct {
config [ ] ConfigEntry
}
// NewDistributionConfig creates new DistributionConfig
func NewDistributionConfig ( ) * DistributionConfig {
config := DistributionConfig { }
return & config
}
// GetLeadersAndShardIDs gets all the leader peers and corresponding shard Ids
func ( config * DistributionConfig ) GetLeadersAndShardIDs ( ) ( [ ] p2p . Peer , [ ] uint32 ) {
var peerList [ ] p2p . Peer
var shardIDs [ ] uint32
for _ , entry := range config . config {
if entry . Role == "leader" {
peerList = append ( peerList , p2p . Peer { IP : entry . IP , Port : entry . Port } )
val , err := strconv . Atoi ( entry . ShardID )
if err == nil {
shardIDs = append ( shardIDs , uint32 ( val ) )
} else {
log . Print ( "[Generator] Error parsing the shard Id " , entry . ShardID )
}
}
}
return peerList , shardIDs
}
// GetClientPeer returns client peer.
func ( config * DistributionConfig ) GetClientPeer ( ) * p2p . Peer {
for _ , entry := range config . config {
if entry . Role != "client" {
continue
}
peer := p2p . Peer { Port : entry . Port , IP : entry . IP }
return & peer
}
return nil
}
// GetClientPort gets the port of the client node in the config
func ( config * DistributionConfig ) GetClientPort ( ) string {
for _ , entry := range config . config {
if entry . Role == "client" {
return entry . Port
}
}
return ""
}
// ReadConfigFile parses the config file and return a 2d array containing the file data
func ( config * DistributionConfig ) ReadConfigFile ( filename string ) error {
file , err := os . Open ( filename )
if err != nil {
log . Fatal ( "Failed to read config file " , filename )
return err
}
defer file . Close ( )
fscanner := bufio . NewScanner ( file )
result := [ ] ConfigEntry { }
validatorMap := map [ int ] int { }
for fscanner . Scan ( ) {
p := strings . Split ( fscanner . Text ( ) , " " )
shardID , _ := strconv . Atoi ( p [ 3 ] )
validatorID := - 1
if p [ 2 ] == "validator" {
validatorMap [ shardID ] ++
validatorID = validatorMap [ shardID ]
}
entry := ConfigEntry { p [ 0 ] , p [ 1 ] , p [ 2 ] , p [ 3 ] , validatorID }
result = append ( result , entry )
}
config . config = result
return nil
}
// GetShardID Gets the shard id of the node corresponding to this ip and port
func ( config * DistributionConfig ) GetShardID ( ip , port string ) string {
for _ , entry := range config . config {
if entry . IP == ip && entry . Port == port {
return entry . ShardID
}
}
return "N/A"
}
// GetPeers Gets the validator list
func ( config * DistributionConfig ) GetPeers ( ip , port , shardID string ) [ ] p2p . Peer {
var peerList [ ] p2p . Peer
for _ , entry := range config . config {
if entry . Role != "validator" || entry . ShardID != shardID {
continue
}
// Get public key deterministically based on ip and port
peer := p2p . Peer { Port : entry . Port , IP : entry . IP , ValidatorID : entry . ValidatorID }
setKey ( & peer )
peerList = append ( peerList , peer )
}
return peerList
}
// GetSelfPeer Gets the validator list
func ( config * DistributionConfig ) GetSelfPeer ( ip , port , shardID string ) p2p . Peer {
for _ , entry := range config . config {
if entry . IP == ip && entry . Port == port && entry . ShardID == shardID {
peer := p2p . Peer { Port : entry . Port , IP : entry . IP , ValidatorID : entry . ValidatorID }
return peer
}
}
return p2p . Peer { }
}
// GetLeader Gets the leader of this shard id
func ( config * DistributionConfig ) GetLeader ( shardID string ) p2p . Peer {
var leaderPeer p2p . Peer
for _ , entry := range config . config {
if entry . Role == "leader" && entry . ShardID == shardID {
leaderPeer . IP = entry . IP
leaderPeer . Port = entry . Port
setKey ( & leaderPeer )
}
}
return leaderPeer
}
// GetConfigEntries returns a list of ConfigEntry.
func ( config * DistributionConfig ) GetConfigEntries ( ) [ ] ConfigEntry {
return config . config
}
// GetMyConfigEntry ...
func ( config * DistributionConfig ) GetMyConfigEntry ( ip string , port string ) * ConfigEntry {
if config . config == nil {
return nil
}
for _ , entry := range config . config {
if entry . IP == ip && entry . Port == port {
return & entry
}
}
return nil
}
func setKey ( peer * p2p . Peer ) {
// Get public key deterministically based on ip and port
priKey := crypto . Ed25519Curve . Scalar ( ) . SetInt64 ( int64 ( GetUniqueIDFromPeer ( * peer ) ) ) // TODO: figure out why using a random hash value doesn't work for private key (schnorr)
peer . PubKey = pki . GetPublicKeyFromScalar ( priKey )
}