Skip to content

Commit

Permalink
multiple genesis and node versions
Browse files Browse the repository at this point in the history
  • Loading branch information
otherview committed Jul 24, 2024
1 parent 9ab27ad commit acb1dc5
Show file tree
Hide file tree
Showing 12 changed files with 340 additions and 143 deletions.
14 changes: 7 additions & 7 deletions environments/local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,17 @@ func (l *Local) LoadConfig(cfg *network.Network) (string, error) {

// ensure paths exist, use temp dirs if not defined
for _, n := range l.networkCfg.Nodes {
if n.ConfigDir == "" {
n.ConfigDir = filepath.Join(baseTmpDir, n.ID, "config")
if n.GetConfigDir() == "" {
n.SetConfigDir(filepath.Join(baseTmpDir, n.GetID(), "config"))
}

if n.DataDir == "" {
n.DataDir = filepath.Join(baseTmpDir, n.ID, "data")
if n.GetDataDir() == "" {
n.SetDataDir(filepath.Join(baseTmpDir, n.GetID(), "data"))
}

// check if the exec artifact path exists
if !fileExists(n.ExecArtifact) {
return "", fmt.Errorf("file does not exist at path: %s", n.ExecArtifact)
if !fileExists(n.GetExecArtifact()) {
return "", fmt.Errorf("file does not exist at path: %s", n.GetExecArtifact())
}
}

Expand All @@ -70,7 +70,7 @@ func (l *Local) StartNetwork() error {
return fmt.Errorf("unable to start node - %w", err)
}

l.localNodes[nodeCfg.ID] = localNode
l.localNodes[nodeCfg.GetID()] = localNode
}

return nil
Expand Down
37 changes: 20 additions & 17 deletions environments/local/local_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ import (
)

type Node struct {
nodeCfg *node.Node
nodeCfg node.Node
cmdExec *exec.Cmd
enodes []string
}

func NewLocalNode(nodeCfg *node.Node, enodes []string) *Node {
func NewLocalNode(nodeCfg node.Node, enodes []string) *Node {
return &Node{
nodeCfg: nodeCfg,
enodes: enodes,
Expand All @@ -29,28 +29,28 @@ func NewLocalNode(nodeCfg *node.Node, enodes []string) *Node {

func (n *Node) Start() error {
// ensure directories exist
if err := os.MkdirAll(n.nodeCfg.ConfigDir, 0777); err != nil {
if err := os.MkdirAll(n.nodeCfg.GetConfigDir(), 0777); err != nil {
return fmt.Errorf("unable to create configDir - %w", err)
}
if err := os.MkdirAll(n.nodeCfg.DataDir, 0777); err != nil {
if err := os.MkdirAll(n.nodeCfg.GetDataDir(), 0777); err != nil {
return fmt.Errorf("unable to create configDir - %w", err)
}

// write keys to disk
if n.nodeCfg.Key != "" {
err := os.WriteFile(filepath.Join(n.nodeCfg.ConfigDir, "master.key"), []byte(n.nodeCfg.Key), 0644)
if n.nodeCfg.GetKey() != "" {
err := os.WriteFile(filepath.Join(n.nodeCfg.GetConfigDir(), "master.key"), []byte(n.nodeCfg.GetKey()), 0644)
if err != nil {
return fmt.Errorf("failed to write master key file - %w", err)
}
err = os.WriteFile(filepath.Join(n.nodeCfg.ConfigDir, "p2p.key"), []byte(n.nodeCfg.Key), 0644)
err = os.WriteFile(filepath.Join(n.nodeCfg.GetConfigDir(), "p2p.key"), []byte(n.nodeCfg.GetKey()), 0644)
if err != nil {
return fmt.Errorf("failed to p2p master key file - %w", err)
}
}

// write genesis to disk
genesisPath := filepath.Join(n.nodeCfg.ConfigDir, "genesis.json")
genesisBytes, err := json.Marshal(n.nodeCfg.Genesis)
genesisPath := filepath.Join(n.nodeCfg.GetConfigDir(), "genesis.json")
genesisBytes, err := json.Marshal(n.nodeCfg.GetGenesis())
if err != nil {
return fmt.Errorf("unable to marshal genesis - %w", err)
}
Expand All @@ -72,28 +72,31 @@ func (n *Node) Start() error {
enodeString := strings.Join(cleanEnode, ",")

cmd := &exec.Cmd{
Path: n.nodeCfg.ExecArtifact,
Path: n.nodeCfg.GetExecArtifact(),
Args: []string{
"thor",
"--network", genesisPath,
"--data-dir", n.nodeCfg.DataDir,
"--config-dir", n.nodeCfg.ConfigDir,
"--api-addr", n.nodeCfg.APIAddr,
"--api-cors", n.nodeCfg.APICORS,
"--data-dir", n.nodeCfg.GetDataDir(),
"--config-dir", n.nodeCfg.GetConfigDir(),
"--api-addr", n.nodeCfg.GetAPIAddr(),
"--api-cors", n.nodeCfg.GetAPICORS(),
"--verbosity", "4",
"--nat", "none",
"--p2p-port", fmt.Sprintf("%d", n.nodeCfg.P2PListenPort),
"--p2p-port", fmt.Sprintf("%d", n.nodeCfg.GetP2PListenPort()),
"--bootnode", enodeString,
},
Stdout: os.Stdout, // Directing stdout to the same stdout of the Go program
Stderr: os.Stderr, // Directing stderr to the same stderr of the Go program
}

if n.nodeCfg.Verbosity != 0 {
cmd.Args = append(cmd.Args, "--verbosity", strconv.Itoa(n.nodeCfg.Verbosity))
if n.nodeCfg.GetVerbosity() != 0 {
cmd.Args = append(cmd.Args, "--verbosity", strconv.Itoa(n.nodeCfg.GetVerbosity()))
}

fmt.Println(cmd)
if n.nodeCfg.GetID() == "node1" {
return nil
}
// Start the command and check for errors
if err := cmd.Start(); err != nil {
return fmt.Errorf("failed to start thor command: %w", err)
Expand Down
4 changes: 2 additions & 2 deletions environments/local/local_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func TestLocalInvalidExecArtifact(t *testing.T) {
)
require.NoError(t, err)

networkCfg.Nodes[0].ExecArtifact = "/some_fake_dir"
networkCfg.Nodes[0].SetExecArtifact("/some_fake_dir")

localEnv := NewLocalEnv()
_, err = localEnv.LoadConfig(networkCfg)
Expand All @@ -153,7 +153,7 @@ func TestLocal(t *testing.T) {
require.NoError(t, err)

time.Sleep(30 * time.Second)
c := client.NewClient("http://" + networkCfg.Nodes[0].APIAddr)
c := client.NewClient("http://" + networkCfg.Nodes[0].GetAPIAddr())
account, err := c.GetAccount(datagen.RandAccount().Address)
require.NoError(t, err)

Expand Down
56 changes: 53 additions & 3 deletions network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import (
)

type Network struct {
Environment string `json:"environment"`
Nodes []*node.Node `json:"nodes"`
ID string `json:"id"`
Environment string `json:"environment"`
Nodes []node.Node `json:"nodes"`
ID string `json:"id"`
}
type Builder struct {
}
Expand All @@ -23,6 +23,7 @@ func WithJSON(s string) BuilderOptionsFunc {
if err != nil {
return err
}

n.Nodes = network.Nodes
n.ID = network.ID
n.Environment = network.Environment
Expand All @@ -39,3 +40,52 @@ func NewNetwork(opts ...BuilderOptionsFunc) (*Network, error) {
}
return n, nil
}

// UnmarshalNode function unmarshals JSON data into the appropriate type based on the presence of VIP212
func UnmarshalNode(data []byte) (node.Node, error) {
var raw map[string]interface{}
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}

var nodeType node.Node
nodeType = &node.NodePreCoefFork{}
if genesis, ok := raw["genesis"].(map[string]interface{}); ok {
if forkConfig, ok := genesis["forkConfig"].(map[string]interface{}); ok {
if _, exists := forkConfig["VIPGASCOEF"]; exists {
nodeType = &node.NodePostCoefFork{}
}
}
}

if err := json.Unmarshal(data, &nodeType); err != nil {
return nil, err
}

return nodeType, nil
}

// UnmarshalJSON implements custom unmarshalling for Network
func (n *Network) UnmarshalJSON(data []byte) error {
type Alias Network
aux := &struct {
Nodes []json.RawMessage `json:"nodes"`
*Alias
}{
Alias: (*Alias)(n),
}

if err := json.Unmarshal(data, &aux); err != nil {
return err
}

for _, nodeData := range aux.Nodes {
nodeObj, err := UnmarshalNode(nodeData)
if err != nil {
return err
}
n.Nodes = append(n.Nodes, nodeObj)
}

return nil
}
27 changes: 27 additions & 0 deletions network/node/genesis/genesis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package genesis

import (
"github.com/vechain/thor/v2/genesis"
)

// CustomGenesis is user customized genesis
type CustomGenesis struct {
LaunchTime uint64 `json:"launchTime"`
GasLimit uint64 `json:"gaslimit"`
ExtraData string `json:"extraData"`
Accounts []genesis.Account `json:"accounts"`
Authority []genesis.Authority `json:"authority"`
Params genesis.Params `json:"params"`
Executor genesis.Executor `json:"executor"`
ForkConfig *ForkConfig `json:"forkConfig"`
}

type ForkConfig struct {
VIP191 uint32
ETH_CONST uint32
BLOCKLIST uint32
ETH_IST uint32
VIP214 uint32
FINALITY uint32
VIPGASCOEF uint32
}
45 changes: 15 additions & 30 deletions network/node/node.go
Original file line number Diff line number Diff line change
@@ -1,38 +1,23 @@
package node

import (
"fmt"

"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/vechain/thor/v2/genesis"
)

const (
MasterNode = "masterNode"
RegularNode = "regularNode"
)

type Node struct {
ID string `json:"id"` //TODO this is a mandatory field
Genesis *genesis.CustomGenesis `json:"genesis,omitempty"` //TODO would be nice to have validation in this format
DataDir string `json:"dataDir,omitempty"`
ConfigDir string `json:"configDir,omitempty"`
P2PListenPort int `json:"p2pListenPort"`
APIAddr string `json:"apiAddr"`
APICORS string `json:"apiCORS"`
Type string `json:"type"`
Key string `json:"key"`
EnodeData string `json:"enode"` // todo: this should be a generated method
ExecArtifact string `json:"execArtifact"` // used to determine the executing version of the node ( path, dockerImage, etc)
Verbosity int `json:"verbosity"`
}

func (n *Node) Enode(ipAddr string) (string, error) {
privKey, err := crypto.HexToECDSA(n.Key)
if err != nil {
return "", fmt.Errorf("unable to process key for node %s : %w", n.ID, err)
}

return fmt.Sprintf("enode://%x@%s:%v", discover.PubkeyID(&privKey.PublicKey).Bytes(), ipAddr, n.P2PListenPort), nil
type Node interface {
Enode(ipAddr string) (string, error)
SetExecArtifact(artifact string)
GetConfigDir() string
SetConfigDir(join string)
GetDataDir() string
SetDataDir(join string)
GetID() string
GetExecArtifact() string
GetKey() string
GetGenesis() any
GetAPIAddr() string
GetAPICORS() string
GetP2PListenPort() int
GetVerbosity() int
}
86 changes: 86 additions & 0 deletions network/node/node_base.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package node

import (
"fmt"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/p2p/discover"
)

type BaseNode struct {
ID string `json:"id"` //TODO this is a mandatory field
Key string `json:"key"`
APIAddr string `json:"apiAddr"`
APICORS string `json:"apiCORS"`
ConfigDir string `json:"configDir,omitempty"`
DataDir string `json:"dataDir,omitempty"`
ExecArtifact string `json:"execArtifact"` // used to determine the executing version of the node ( path, dockerImage, etc)
P2PListenPort int `json:"p2pListenPort"`
Verbosity int `json:"verbosity"`
EnodeData string `json:"enode"` // todo: this should be a generated method
Type string `json:"type"`
}

func (b *BaseNode) GetVerbosity() int {
return b.Verbosity
}

func (b *BaseNode) GetP2PListenPort() int {
return b.P2PListenPort
}

func (b *BaseNode) GetAPIAddr() string {
return b.APIAddr
}

func (b *BaseNode) GetAPICORS() string {
return b.APICORS
}

func (b *BaseNode) GetGenesis() any {
return b.GetExecArtifact()
}

func (b *BaseNode) GetKey() string {
return b.Key
}

func New() Node {
return &BaseNode{}
}

func (b *BaseNode) GetConfigDir() string {
return b.ConfigDir
}

func (b *BaseNode) SetConfigDir(s string) {
b.ConfigDir = s
}

func (b *BaseNode) GetDataDir() string {
return b.DataDir
}

func (b *BaseNode) SetDataDir(s string) {
b.DataDir = s
}

func (b *BaseNode) GetID() string {
return b.ID
}

func (b *BaseNode) GetExecArtifact() string {
return b.ExecArtifact
}

func (b *BaseNode) SetExecArtifact(artifact string) {
b.ExecArtifact = artifact
}

func (b *BaseNode) Enode(ipAddr string) (string, error) {
privKey, err := crypto.HexToECDSA(b.Key)
if err != nil {
return "", fmt.Errorf("unable to process key for node %s : %w", b.ID, err)
}

return fmt.Sprintf("enode://%x@%s:%v", discover.PubkeyID(&privKey.PublicKey).Bytes(), ipAddr, b.P2PListenPort), nil
}
14 changes: 14 additions & 0 deletions network/node/node_post_coef_fork.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package node

import (
"github.com/vechain/networkhub/network/node/genesis"
)

type NodePostCoefFork struct {
BaseNode
Genesis *genesis.CustomGenesis `json:"genesis,omitempty"` //TODO would be nice to have validation in this format
}

func (n *NodePostCoefFork) GetGenesis() any {
return n.Genesis
}
Loading

0 comments on commit acb1dc5

Please sign in to comment.