Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(preset): galactica network #12

Closed
wants to merge 11 commits into from
127 changes: 120 additions & 7 deletions environments/local/local_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,22 @@ package local
import (
"encoding/json"
"fmt"
"math"
"strings"
"testing"
"time"

"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
"github.com/vechain/networkhub/network"
"github.com/vechain/networkhub/network/node"
"github.com/vechain/networkhub/preset"
"github.com/vechain/networkhub/utils/client"
"github.com/vechain/networkhub/utils/common"
"github.com/vechain/networkhub/utils/datagen"
"github.com/vechain/thor/v2/thor"
"github.com/vechain/thor/v2/tx"
)

var genesis = `{
Expand Down Expand Up @@ -208,7 +215,7 @@ func TestSixNode(t *testing.T) {

// ensure the artifact path is set
for _, node := range networkCfg.Nodes {
node.SetExecArtifact("/Users/pedro/go/src/github.com/vechain/thor/bin/thor")
node.SetExecArtifact("/Users/user/go/src/github.com/vechain/thor/bin/thor")
}

localEnv := NewLocalEnv()
Expand All @@ -217,15 +224,121 @@ func TestSixNode(t *testing.T) {

err = localEnv.StartNetwork()
require.NoError(t, err)
defer func() {
err = localEnv.StopNetwork()
require.NoError(t, err)
}()

time.Sleep(30 * time.Second) // todo change this to a polling approach
for _, node := range networkCfg.Nodes {
c := client.NewClient("http://" + node.GetAPIAddr())
peers, err := c.GetPeers()
pollingWhileConnectingPeers(t, networkCfg.Nodes, 5)
}

func TestFourNodesGalactica(t *testing.T) {
fourNodesGalacticaJson, err := json.Marshal(preset.LocalFourNodesGalacticaNetwork)
require.NoError(t, err)

networkCfg, err := network.NewNetwork(
network.WithJSON(string(fourNodesGalacticaJson)),
)
require.NoError(t, err)

localEnv := NewLocalEnv()
_, err = localEnv.LoadConfig(networkCfg)
require.NoError(t, err)

err = localEnv.StartNetwork()
require.NoError(t, err)
defer func() {
err = localEnv.StopNetwork()
require.NoError(t, err)
}()

clients := pollingWhileConnectingPeers(t, networkCfg.Nodes, 3)

deployAndAssertShanghaiContract(t, clients[0], preset.Account1)
}

func pollingWhileConnectingPeers(t *testing.T, nodes []node.Node, expectedPeersLen int) []*client.Client {
// Polling approach with timeout
timeout := time.After(1 * time.Minute)
tick := time.Tick(5 * time.Second)

require.GreaterOrEqual(t, len(peers), 0)
clients := make([]*client.Client, 0)
outer:
for {
select {
case <-timeout:
t.Fatal("timed out waiting for nodes to connect")
case <-tick:
for _, node := range nodes {
c := client.NewClient("http://" + node.GetAPIAddr())
peers, err := c.GetPeers()
require.True(t, err == nil && len(peers) == expectedPeersLen)
clients = append(clients, c)
}
break outer
}
}
err = localEnv.StopNetwork()
return clients
}

// https://github.com/vechain/thor-e2e-tests/blob/main/contracts/shanghai/SimpleCounterShanghai.sol
const shanghaiContractBytecode = "0x608060405234801561000f575f80fd5b505f805561016e806100205f395ff3fe608060405234801561000f575f80fd5b506004361061003f575f3560e01c80635b34b966146100435780638ada066e1461004d5780638bb5d9c314610061575b5f80fd5b61004b610074565b005b5f5460405190815260200160405180910390f35b61004b61006f3660046100fd565b6100c3565b5f8054908061008283610114565b91905055507f3cf8b50771c17d723f2cb711ca7dadde485b222e13c84ba0730a14093fad6d5c5f546040516100b991815260200190565b60405180910390a1565b5f8190556040518181527f3cf8b50771c17d723f2cb711ca7dadde485b222e13c84ba0730a14093fad6d5c9060200160405180910390a150565b5f6020828403121561010d575f80fd5b5035919050565b5f6001820161013157634e487b7160e01b5f52601160045260245ffd5b506001019056fea2646970667358221220aa73e6082b52bca8243902c639e5386b481c2183e8400f34731c4edb93d87f6764736f6c63430008180033"

func decodedShanghaiContract(t *testing.T) []byte {
contractBytecode, err := hexutil.Decode(shanghaiContractBytecode)
require.NoError(t, err)
return contractBytecode
}

func deployAndAssertShanghaiContract(t *testing.T, client *client.Client, acc *common.Account) {
tag, err := client.ChainTag()
require.NoError(t, err)

// Build the transaction using the bytecode
contractData := decodedShanghaiContract(t)

deployContractTx := new(tx.Builder).
ChainTag(tag).
Expiration(math.MaxUint32).
Gas(10_000_000).
GasPriceCoef(128).
BlockRef(tx.NewBlockRef(0)).
Nonce(datagen.RandUInt64()).
Clause(
tx.NewClause(nil).WithData(contractData),
).Build()

// Simulating the contract deployment transaction before deploying it
depContractInspectResults, err := client.InspectTxClauses(deployContractTx, acc.Address)
require.NoError(t, err)
for _, respClause := range depContractInspectResults {
require.False(t, respClause.Reverted || respClause.VMError != "")
}

// Send a transaction
signedTxHash, err := crypto.Sign(deployContractTx.SigningHash().Bytes(), acc.PrivateKey)
require.NoError(t, err)
issuedTx, err := client.SendTransaction(deployContractTx.WithSignature(signedTxHash))
require.NoError(t, err)

// Retrieve transaction receipt - GET /transactions/{id}/receipt
var contractAddr *thor.Address
const retryPeriod = 3 * time.Second
const maxRetries = 8
err = common.Retry(func() error {
receipt, err := client.GetTransactionReceipt(issuedTx.ID)
if err != nil {
return fmt.Errorf("unable to retrieve tx receipt - %w", err)
}

if receipt.Reverted {
return fmt.Errorf("transaction was reverted - %+v", receipt)
}

contractAddr = receipt.Outputs[0].ContractAddress
return nil
}, retryPeriod, maxRetries)

require.NoError(t, err)
require.NotNil(t, contractAddr)
}
2 changes: 2 additions & 0 deletions network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ func UnmarshalNode(data []byte) (node.Node, error) {
if forkConfig, ok := genesis["forkConfig"].(map[string]interface{}); ok {
if _, exists := forkConfig["VIPGASCOEF"]; exists {
nodeType = &node.NodePostCoefFork{}
} else if _, exists := forkConfig["GALACTICA"]; exists {
nodeType = &node.NodeGalacticaFork{}
}
}
}
Expand Down
25 changes: 25 additions & 0 deletions network/node/genesis/genesis_galactica_fork.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package genesis

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

// GalacticaForkGenesis is user customized genesis
type GalacticaForkGenesis 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 *GalacticaForkConfig `json:"forkConfig"`
}

type GalacticaForkConfig struct {
VIP191 uint32
ETH_CONST uint32
BLOCKLIST uint32
ETH_IST uint32
VIP214 uint32
FINALITY uint32
GALACTICA uint32
}
14 changes: 14 additions & 0 deletions network/node/node_galactica_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 NodeGalacticaFork struct {
BaseNode
Genesis *genesis.GalacticaForkGenesis `json:"genesis,omitempty"` //TODO would be nice to have validation in this format
}

func (n *NodeGalacticaFork) GetGenesis() any {
return n.Genesis
}
171 changes: 171 additions & 0 deletions preset/LocalFourNodesGalactica.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package preset

import (
"math/big"

"github.com/vechain/networkhub/consts"
"github.com/vechain/networkhub/environments"
"github.com/vechain/networkhub/network"
"github.com/vechain/networkhub/network/node"
"github.com/vechain/networkhub/network/node/genesis"
"github.com/vechain/networkhub/thorbuilder"
"github.com/vechain/thor/v2/thor"

thorgenesis "github.com/vechain/thor/v2/genesis"
)

func getGalacticaExecArtifact() string {
thorBuilder := thorbuilder.New("release/galactica")
if err := thorBuilder.Download(); err != nil {
panic(err)
}

path, err := thorBuilder.Build()
if err != nil {
panic(err)
}

return path
}

var galacticaExecArtifact string
var LocalFourNodesGalacticaNetwork *network.Network

func init() {
galacticaExecArtifact = getGalacticaExecArtifact()
LocalFourNodesGalacticaNetwork = &network.Network{
ID: "fourNodesGalacticaNetwork",
Environment: environments.Local,
Nodes: []node.Node{
&node.NodeGalacticaFork{
BaseNode: node.BaseNode{
ID: "node1",
P2PListenPort: 8081,
APIAddr: "127.0.0.1:8181",
APICORS: "*",
Type: node.MasterNode,
Verbosity: 4,
Key: "b2c859e115ef4a3f5e4d32228b41de4c661c527a32f723ac37745bf860fd09cb", // 0x5F90f56c7b87E3d1acf9437f0E43E4d687AcEB7e
ExecArtifact: galacticaExecArtifact,
},
Genesis: localFourNodesNetworkGenesis,
},
&node.NodeGalacticaFork{
BaseNode: node.BaseNode{
ID: "node2",
P2PListenPort: 8082,
APIAddr: "127.0.0.1:8182",
APICORS: "*",
Type: node.MasterNode,
Verbosity: 4,
Key: "4de650ca1c8beae4ed6a4358087f50c01b51f5c0002ae9836c55039ca9818d0c", // 0x5c29518F6a6124a2BeE89253347c8295f604710A
ExecArtifact: galacticaExecArtifact,
},
Genesis: localFourNodesNetworkGenesis,
},
&node.NodeGalacticaFork{
BaseNode: node.BaseNode{
ID: "node3",
P2PListenPort: 8083,
APIAddr: "127.0.0.1:8183",
APICORS: "*",
Type: node.RegularNode,
Key: "1b310ea04afd6d14a8f142158873fc70bfd4ba12a19138cc5b309fce7c77105e", // 0x1b1c0055065b3ADee4B9a9e8297142Ba2cD34EfE
ExecArtifact: galacticaExecArtifact,
},
Genesis: localFourNodesNetworkGenesis,
},
&node.NodeGalacticaFork{
BaseNode: node.BaseNode{
ID: "node4",
P2PListenPort: 8084,
APIAddr: "127.0.0.1:8184",
APICORS: "*",
Type: node.MasterNode,
Verbosity: 4,
Key: "c70dda88e779df10abbc7c5d37fbb3478c5cf8df2a70d6b0bfc551a5a9a17359", // 0x042306e116Dc301ecd7b83a04F4c8277Fbe41b6c
ExecArtifact: galacticaExecArtifact,
},
Genesis: localFourNodesNetworkGenesis,
},
},
}
}

var localFourNodesNetworkGenesis = &genesis.GalacticaForkGenesis{
LaunchTime: 1703180212,
GasLimit: 10000000,
ExtraData: "Local Four Nodes Network (Galactica)",
Accounts: []thorgenesis.Account{
{
Address: thor.MustParseAddress("0x7567d83b7b8d80addcb281a71d54fc7b3364ffed"),
Balance: convToHexOrDecimal256(consts.LargeBigValue),
Energy: convToHexOrDecimal256(consts.LargeBigValue),
},
{
Address: *Account1.Address,
Balance: convToHexOrDecimal256(consts.LargeBigValue),
Energy: convToHexOrDecimal256(consts.LargeBigValue),
},
{
Address: *Account2.Address,
Balance: convToHexOrDecimal256(consts.LargeBigValue),
Energy: convToHexOrDecimal256(consts.LargeBigValue),
},
{
Address: *Account3.Address,
Balance: convToHexOrDecimal256(consts.LargeBigValue),
Energy: convToHexOrDecimal256(consts.LargeBigValue),
},
{
Address: *Account4.Address,
Balance: convToHexOrDecimal256(consts.LargeBigValue),
Energy: convToHexOrDecimal256(consts.LargeBigValue),
},
},
Authority: []thorgenesis.Authority{
{
MasterAddress: *Account1.Address,
EndorsorAddress: thor.MustParseAddress("0x7567d83b7b8d80addcb281a71d54fc7b3364ffed"),
Identity: thor.MustParseBytes32("0x000000000000000068747470733a2f2f636f6e6e65782e76656368612e696e2f"),
},
{
MasterAddress: thor.MustParseAddress("0x5c29518F6a6124a2BeE89253347c8295f604710A"),
EndorsorAddress: thor.MustParseAddress("0x7567d83b7b8d80addcb281a71d54fc7b3364ffed"),
Identity: thor.MustParseBytes32("0x000000000000000068747470733a2f2f656e762e7665636861696e2e6f72672f"),
},
{
MasterAddress: thor.MustParseAddress("0x042306e116Dc301ecd7b83a04F4c8277Fbe41b6c"),
EndorsorAddress: thor.MustParseAddress("0x7567d83b7b8d80addcb281a71d54fc7b3364ffed"),
Identity: thor.MustParseBytes32("0x0000000000000068747470733a2f2f617070732e7665636861696e2e6f72672f"),
},
{
MasterAddress: thor.MustParseAddress("0x0aeC31606e217895696771961de416Efa185Be66"),
EndorsorAddress: thor.MustParseAddress("0x7567d83b7b8d80addcb281a71d54fc7b3364ffed"),
Identity: thor.MustParseBytes32("0x0000000000000068747470733a2f2f617070732e7665636861696e2e6f72672f"),
},
},
Params: thorgenesis.Params{
RewardRatio: convToHexOrDecimal256(big.NewInt(300000000000000000)),
BaseGasPrice: convToHexOrDecimal256(big.NewInt(1000000000000000)),
ProposerEndorsement: convToHexOrDecimal256(consts.LargeBigValue),
ExecutorAddress: &localThreeMasterEndorser,
},
Executor: thorgenesis.Executor{
Approvers: []thorgenesis.Approver{
{
Address: thor.MustParseAddress("0x199b836d8a57365baccd4f371c1fabb7be77d389"),
Identity: thor.MustParseBytes32("0x00000000000067656e6572616c20707572706f736520626c6f636b636861696e"),
},
},
},
ForkConfig: &genesis.GalacticaForkConfig{
VIP191: 0,
ETH_CONST: 0,
BLOCKLIST: 0,
ETH_IST: 0,
VIP214: 0,
FINALITY: 0,
GALACTICA: 0,
},
}
Loading
Loading