mirror of
https://github.com/luxfi/oraclevm.git
synced 2026-10-30 15:23:15 +00:00
784 lines
21 KiB
Go
784 lines
21 KiB
Go
// Copyright (C) 2023, Ava Labs, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package e2e_test
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
runner_sdk "github.com/ava-labs/avalanche-network-runner/client"
|
|
"github.com/ava-labs/avalanche-network-runner/rpcpb"
|
|
"github.com/ava-labs/avalanchego/config"
|
|
"github.com/ava-labs/avalanchego/ids"
|
|
"github.com/ava-labs/avalanchego/utils/logging"
|
|
"github.com/ava-labs/hypersdk/crypto"
|
|
"github.com/ava-labs/hypersdk/rpc"
|
|
hutils "github.com/ava-labs/hypersdk/utils"
|
|
"github.com/bianyuanop/oraclevm/actions"
|
|
"github.com/bianyuanop/oraclevm/auth"
|
|
"github.com/bianyuanop/oraclevm/consts"
|
|
lrpc "github.com/bianyuanop/oraclevm/rpc"
|
|
"github.com/bianyuanop/oraclevm/utils"
|
|
"github.com/fatih/color"
|
|
ginkgo "github.com/onsi/ginkgo/v2"
|
|
"github.com/onsi/gomega"
|
|
)
|
|
|
|
const (
|
|
startAmount = uint64(1000000000000)
|
|
sendAmount = uint64(5000)
|
|
|
|
healthPollInterval = 10 * time.Second
|
|
)
|
|
|
|
func TestE2e(t *testing.T) {
|
|
gomega.RegisterFailHandler(ginkgo.Fail)
|
|
ginkgo.RunSpecs(t, "morpheusvm e2e test suites")
|
|
}
|
|
|
|
var (
|
|
requestTimeout time.Duration
|
|
|
|
networkRunnerLogLevel string
|
|
gRPCEp string
|
|
gRPCGatewayEp string
|
|
|
|
execPath string
|
|
pluginDir string
|
|
|
|
vmGenesisPath string
|
|
vmConfigPath string
|
|
vmConfig string
|
|
subnetConfigPath string
|
|
outputPath string
|
|
|
|
mode string
|
|
|
|
logsDir string
|
|
|
|
blockchainID string
|
|
|
|
trackSubnetsOpt runner_sdk.OpOption
|
|
)
|
|
|
|
func init() {
|
|
flag.DurationVar(
|
|
&requestTimeout,
|
|
"request-timeout",
|
|
120*time.Second,
|
|
"timeout for transaction issuance and confirmation",
|
|
)
|
|
|
|
flag.StringVar(
|
|
&networkRunnerLogLevel,
|
|
"network-runner-log-level",
|
|
"info",
|
|
"gRPC server endpoint",
|
|
)
|
|
|
|
flag.StringVar(
|
|
&gRPCEp,
|
|
"network-runner-grpc-endpoint",
|
|
"0.0.0.0:8080",
|
|
"gRPC server endpoint",
|
|
)
|
|
flag.StringVar(
|
|
&gRPCGatewayEp,
|
|
"network-runner-grpc-gateway-endpoint",
|
|
"0.0.0.0:8081",
|
|
"gRPC gateway endpoint",
|
|
)
|
|
|
|
flag.StringVar(
|
|
&execPath,
|
|
"avalanchego-path",
|
|
"",
|
|
"avalanchego executable path",
|
|
)
|
|
|
|
flag.StringVar(
|
|
&pluginDir,
|
|
"avalanchego-plugin-dir",
|
|
"",
|
|
"avalanchego plugin directory",
|
|
)
|
|
|
|
flag.StringVar(
|
|
&vmGenesisPath,
|
|
"vm-genesis-path",
|
|
"",
|
|
"VM genesis file path",
|
|
)
|
|
|
|
flag.StringVar(
|
|
&vmConfigPath,
|
|
"vm-config-path",
|
|
"",
|
|
"VM configfile path",
|
|
)
|
|
|
|
flag.StringVar(
|
|
&subnetConfigPath,
|
|
"subnet-config-path",
|
|
"",
|
|
"Subnet configfile path",
|
|
)
|
|
|
|
flag.StringVar(
|
|
&outputPath,
|
|
"output-path",
|
|
"",
|
|
"output YAML path to write local cluster information",
|
|
)
|
|
|
|
flag.StringVar(
|
|
&mode,
|
|
"mode",
|
|
"test",
|
|
"'test' to shut down cluster after tests, 'run' to skip tests and only run without shutdown",
|
|
)
|
|
}
|
|
|
|
const (
|
|
modeTest = "test"
|
|
modeFullTest = "full-test" // runs state sync
|
|
modeRun = "run"
|
|
)
|
|
|
|
var anrCli runner_sdk.Client
|
|
|
|
var _ = ginkgo.BeforeSuite(func() {
|
|
gomega.Expect(mode).Should(gomega.Or(
|
|
gomega.Equal(modeTest),
|
|
gomega.Equal(modeFullTest),
|
|
gomega.Equal(modeRun),
|
|
))
|
|
logLevel, err := logging.ToLevel(networkRunnerLogLevel)
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
logFactory := logging.NewFactory(logging.Config{
|
|
DisplayLevel: logLevel,
|
|
LogLevel: logLevel,
|
|
})
|
|
log, err := logFactory.Make("main")
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
|
|
anrCli, err = runner_sdk.New(runner_sdk.Config{
|
|
Endpoint: gRPCEp,
|
|
DialTimeout: 10 * time.Second,
|
|
}, log)
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
|
|
// Load default pk
|
|
priv, err = crypto.HexToKey(
|
|
"323b1d8f4eed5f0da9da93071b034f2dce9d2d22692c172f3cb252a64ddfafd01b057de320297c29ad0c1f589ea216869cf1938d88c9fbd70d6748323dbf2fa7", //nolint:lll
|
|
)
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
factory = auth.NewED25519Factory(priv)
|
|
rsender = priv.PublicKey()
|
|
sender = utils.Address(rsender)
|
|
hutils.Outf("\n{{yellow}}$ loaded address:{{/}} %s\n\n", sender)
|
|
|
|
hutils.Outf(
|
|
"{{green}}sending 'start' with binary path:{{/}} %q (%q)\n",
|
|
execPath,
|
|
consts.ID,
|
|
)
|
|
|
|
// Load config data
|
|
if len(vmConfigPath) > 0 {
|
|
configData, err := os.ReadFile(vmConfigPath)
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
vmConfig = string(configData)
|
|
} else {
|
|
vmConfig = "{}"
|
|
}
|
|
|
|
// Start cluster
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
|
resp, err := anrCli.Start(
|
|
ctx,
|
|
execPath,
|
|
runner_sdk.WithPluginDir(pluginDir),
|
|
// We don't disable PUT gossip here because the E2E test adds multiple
|
|
// non-validating nodes (which will fall behind).
|
|
runner_sdk.WithGlobalNodeConfig(`{
|
|
"log-display-level":"info",
|
|
"proposervm-use-current-height":true,
|
|
"throttler-inbound-validator-alloc-size":"10737418240",
|
|
"throttler-inbound-at-large-alloc-size":"10737418240",
|
|
"throttler-inbound-node-max-processing-msgs":"100000",
|
|
"throttler-inbound-bandwidth-refill-rate":"1073741824",
|
|
"throttler-inbound-bandwidth-max-burst-size":"1073741824",
|
|
"throttler-inbound-cpu-validator-alloc":"100000",
|
|
"throttler-inbound-disk-validator-alloc":"10737418240000",
|
|
"throttler-outbound-validator-alloc-size":"10737418240",
|
|
"throttler-outbound-at-large-alloc-size":"10737418240",
|
|
"consensus-on-accept-gossip-validator-size":"10",
|
|
"consensus-on-accept-gossip-peer-size":"10",
|
|
"network-compression-type":"zstd",
|
|
"consensus-app-concurrency":"512",
|
|
"profile-continuous-enabled":true,
|
|
"profile-continuous-freq":"1m"
|
|
}`),
|
|
)
|
|
cancel()
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
hutils.Outf(
|
|
"{{green}}successfully started cluster:{{/}} %s {{green}}subnets:{{/}} %+v\n",
|
|
resp.ClusterInfo.RootDataDir,
|
|
resp.GetClusterInfo().GetSubnets(),
|
|
)
|
|
logsDir = resp.GetClusterInfo().GetRootDataDir()
|
|
|
|
// Name 5 new validators (which should have BLS key registered)
|
|
subnet := []string{}
|
|
for i := 1; i <= 5; i++ {
|
|
n := fmt.Sprintf("node%d-bls", i)
|
|
subnet = append(subnet, n)
|
|
}
|
|
specs := []*rpcpb.BlockchainSpec{
|
|
{
|
|
VmName: consts.Name,
|
|
Genesis: vmGenesisPath,
|
|
ChainConfig: vmConfigPath,
|
|
SubnetSpec: &rpcpb.SubnetSpec{
|
|
SubnetConfig: subnetConfigPath,
|
|
Participants: subnet,
|
|
},
|
|
},
|
|
}
|
|
|
|
// Create subnet
|
|
ctx, cancel = context.WithTimeout(context.Background(), 5*time.Minute)
|
|
sresp, err := anrCli.CreateBlockchains(
|
|
ctx,
|
|
specs,
|
|
)
|
|
cancel()
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
|
|
blockchainID = sresp.ChainIds[0]
|
|
subnetID := sresp.ClusterInfo.CustomChains[blockchainID].SubnetId
|
|
hutils.Outf(
|
|
"{{green}}successfully added chain:{{/}} %s {{green}}subnet:{{/}} %s {{green}}participants:{{/}} %+v\n",
|
|
blockchainID,
|
|
subnetID,
|
|
subnet,
|
|
)
|
|
trackSubnetsOpt = runner_sdk.WithGlobalNodeConfig(fmt.Sprintf(`{"%s":"%s"}`,
|
|
config.TrackSubnetsKey,
|
|
subnetID,
|
|
))
|
|
|
|
gomega.Expect(blockchainID).Should(gomega.Not(gomega.BeEmpty()))
|
|
gomega.Expect(logsDir).Should(gomega.Not(gomega.BeEmpty()))
|
|
|
|
cctx, ccancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
|
status, err := anrCli.Status(cctx)
|
|
ccancel()
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
nodeInfos := status.GetClusterInfo().GetNodeInfos()
|
|
|
|
instances = []instance{}
|
|
for _, nodeName := range subnet {
|
|
info := nodeInfos[nodeName]
|
|
u := fmt.Sprintf("%s/ext/bc/%s", info.Uri, blockchainID)
|
|
bid, err := ids.FromString(blockchainID)
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
nodeID, err := ids.NodeIDFromString(info.GetId())
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
cli := rpc.NewJSONRPCClient(u)
|
|
|
|
// After returning healthy, the node may not respond right away
|
|
//
|
|
// TODO: figure out why
|
|
var networkID uint32
|
|
for i := 0; i < 10; i++ {
|
|
networkID, _, _, err = cli.Network(context.TODO())
|
|
if err != nil {
|
|
time.Sleep(1 * time.Second)
|
|
continue
|
|
}
|
|
}
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
|
|
instances = append(instances, instance{
|
|
nodeID: nodeID,
|
|
uri: u,
|
|
cli: cli,
|
|
lcli: lrpc.NewJSONRPCClient(u, networkID, bid),
|
|
})
|
|
}
|
|
})
|
|
|
|
var (
|
|
priv crypto.PrivateKey
|
|
factory *auth.ED25519Factory
|
|
rsender crypto.PublicKey
|
|
sender string
|
|
|
|
instances []instance
|
|
)
|
|
|
|
type instance struct {
|
|
nodeID ids.NodeID
|
|
uri string
|
|
cli *rpc.JSONRPCClient
|
|
lcli *lrpc.JSONRPCClient
|
|
}
|
|
|
|
var _ = ginkgo.AfterSuite(func() {
|
|
switch mode {
|
|
case modeTest, modeFullTest:
|
|
hutils.Outf("{{red}}shutting down cluster{{/}}\n")
|
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
|
_, err := anrCli.Stop(ctx)
|
|
cancel()
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
|
|
case modeRun:
|
|
hutils.Outf("{{yellow}}skipping cluster shutdown{{/}}\n\n")
|
|
hutils.Outf("{{cyan}}Blockchain:{{/}} %s\n", blockchainID)
|
|
for _, member := range instances {
|
|
hutils.Outf("%s URI: %s\n", member.nodeID, member.uri)
|
|
}
|
|
}
|
|
gomega.Expect(anrCli.Close()).Should(gomega.BeNil())
|
|
})
|
|
|
|
var _ = ginkgo.Describe("[Ping]", func() {
|
|
ginkgo.It("can ping A", func() {
|
|
for _, inst := range instances {
|
|
cli := inst.cli
|
|
ok, err := cli.Ping(context.Background())
|
|
gomega.Ω(ok).Should(gomega.BeTrue())
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
}
|
|
})
|
|
})
|
|
|
|
var _ = ginkgo.Describe("[Network]", func() {
|
|
ginkgo.It("can get network", func() {
|
|
for _, inst := range instances {
|
|
cli := inst.cli
|
|
networkID, _, chainID, err := cli.Network(context.Background())
|
|
gomega.Ω(networkID).Should(gomega.Equal(uint32(1337)))
|
|
gomega.Ω(chainID).ShouldNot(gomega.Equal(ids.Empty))
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
}
|
|
})
|
|
})
|
|
|
|
var _ = ginkgo.Describe("[Test]", func() {
|
|
switch mode {
|
|
case modeRun:
|
|
hutils.Outf("{{yellow}}skipping tests{{/}}\n")
|
|
return
|
|
}
|
|
|
|
ginkgo.It("get currently accepted block ID", func() {
|
|
for _, inst := range instances {
|
|
cli := inst.cli
|
|
_, h, _, err := cli.Accepted(context.Background())
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
gomega.Ω(h).Should(gomega.Equal(uint64(0)))
|
|
}
|
|
})
|
|
|
|
ginkgo.It("transfer in a single node (raw)", func() {
|
|
nativeBalance, err := instances[0].lcli.Balance(context.TODO(), sender)
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
gomega.Ω(nativeBalance).Should(gomega.Equal(uint64(1000000000000)))
|
|
|
|
other, err := crypto.GeneratePrivateKey()
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
aother := utils.Address(other.PublicKey())
|
|
|
|
ginkgo.By("issue Transfer to the first node", func() {
|
|
// Generate transaction
|
|
parser, err := instances[0].lcli.Parser(context.TODO())
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
submit, tx, fee, err := instances[0].cli.GenerateTransaction(
|
|
context.Background(),
|
|
parser,
|
|
nil,
|
|
&actions.Transfer{
|
|
To: other.PublicKey(),
|
|
Value: sendAmount,
|
|
},
|
|
factory,
|
|
)
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
hutils.Outf("{{yellow}}generated transaction{{/}}\n")
|
|
|
|
// Broadcast and wait for transaction
|
|
gomega.Ω(submit(context.Background())).Should(gomega.BeNil())
|
|
hutils.Outf("{{yellow}}submitted transaction{{/}}\n")
|
|
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
|
|
success, err := instances[0].lcli.WaitForTransaction(ctx, tx.ID())
|
|
cancel()
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
gomega.Ω(success).Should(gomega.BeTrue())
|
|
hutils.Outf("{{yellow}}found transaction{{/}}\n")
|
|
|
|
// Check sender balance
|
|
balance, err := instances[0].lcli.Balance(context.Background(), sender)
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
hutils.Outf(
|
|
"{{yellow}}start=%d fee=%d send=%d balance=%d{{/}}\n",
|
|
startAmount,
|
|
fee,
|
|
sendAmount,
|
|
balance,
|
|
)
|
|
gomega.Ω(balance).Should(gomega.Equal(startAmount - fee - sendAmount))
|
|
hutils.Outf("{{yellow}}fetched balance{{/}}\n")
|
|
})
|
|
|
|
ginkgo.By("check if Transfer has been accepted from all nodes", func() {
|
|
for _, inst := range instances {
|
|
color.Blue("checking %q", inst.uri)
|
|
|
|
// Ensure all blocks processed
|
|
for {
|
|
_, h, _, err := inst.cli.Accepted(context.Background())
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
if h > 0 {
|
|
break
|
|
}
|
|
time.Sleep(1 * time.Second)
|
|
}
|
|
|
|
// Check balance of recipient
|
|
balance, err := inst.lcli.Balance(context.Background(), aother)
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
gomega.Ω(balance).Should(gomega.Equal(sendAmount))
|
|
}
|
|
})
|
|
})
|
|
|
|
switch mode {
|
|
case modeTest:
|
|
hutils.Outf("{{yellow}}skipping bootstrap and state sync tests{{/}}\n")
|
|
return
|
|
}
|
|
|
|
// Create blocks before bootstrapping starts
|
|
count := 0
|
|
ginkgo.It("supports issuance of 128 blocks", func() {
|
|
count += generateBlocks(context.Background(), count, 128, instances, true)
|
|
})
|
|
|
|
// Ensure bootstrapping works
|
|
var syncClient *rpc.JSONRPCClient
|
|
var lsyncClient *lrpc.JSONRPCClient
|
|
ginkgo.It("can bootstrap a new node", func() {
|
|
cluster, err := anrCli.AddNode(
|
|
context.Background(),
|
|
"bootstrap",
|
|
execPath,
|
|
trackSubnetsOpt,
|
|
runner_sdk.WithChainConfigs(map[string]string{
|
|
blockchainID: vmConfig,
|
|
}),
|
|
)
|
|
gomega.Expect(err).To(gomega.BeNil())
|
|
awaitHealthy(anrCli, []instance{instances[0]})
|
|
|
|
nodeURI := cluster.ClusterInfo.NodeInfos["bootstrap"].Uri
|
|
uri := nodeURI + fmt.Sprintf("/ext/bc/%s", blockchainID)
|
|
bid, err := ids.FromString(blockchainID)
|
|
gomega.Expect(err).To(gomega.BeNil())
|
|
hutils.Outf("{{blue}}bootstrap node uri: %s{{/}}\n", uri)
|
|
c := rpc.NewJSONRPCClient(uri)
|
|
syncClient = c
|
|
networkID, _, _, err := syncClient.Network(context.TODO())
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
tc := lrpc.NewJSONRPCClient(uri, networkID, bid)
|
|
lsyncClient = tc
|
|
instances = append(instances, instance{
|
|
uri: uri,
|
|
cli: c,
|
|
lcli: tc,
|
|
})
|
|
})
|
|
|
|
ginkgo.It("accepts transaction after it bootstraps", func() {
|
|
acceptTransaction(syncClient, lsyncClient)
|
|
})
|
|
|
|
// Create blocks before state sync starts (state sync requires at least 256
|
|
// blocks)
|
|
//
|
|
// We do 1024 so that there are a number of ranges of data to fetch.
|
|
ginkgo.It("supports issuance of at least 1024 more blocks", func() {
|
|
count += generateBlocks(context.Background(), count, 1024, instances, true)
|
|
// TODO: verify all roots are equal
|
|
})
|
|
|
|
ginkgo.It("can state sync a new node when no new blocks are being produced", func() {
|
|
cluster, err := anrCli.AddNode(
|
|
context.Background(),
|
|
"sync",
|
|
execPath,
|
|
trackSubnetsOpt,
|
|
runner_sdk.WithChainConfigs(map[string]string{
|
|
blockchainID: vmConfig,
|
|
}),
|
|
)
|
|
gomega.Expect(err).To(gomega.BeNil())
|
|
|
|
awaitHealthy(anrCli, []instance{instances[0]})
|
|
|
|
nodeURI := cluster.ClusterInfo.NodeInfos["sync"].Uri
|
|
uri := nodeURI + fmt.Sprintf("/ext/bc/%s", blockchainID)
|
|
bid, err := ids.FromString(blockchainID)
|
|
gomega.Expect(err).To(gomega.BeNil())
|
|
hutils.Outf("{{blue}}sync node uri: %s{{/}}\n", uri)
|
|
syncClient = rpc.NewJSONRPCClient(uri)
|
|
networkID, _, _, err := syncClient.Network(context.TODO())
|
|
gomega.Expect(err).To(gomega.BeNil())
|
|
lsyncClient = lrpc.NewJSONRPCClient(uri, networkID, bid)
|
|
})
|
|
|
|
ginkgo.It("accepts transaction after state sync", func() {
|
|
acceptTransaction(syncClient, lsyncClient)
|
|
})
|
|
|
|
ginkgo.It("can pause a node", func() {
|
|
// shuts down the node and keeps all db/conf data for a proper restart
|
|
_, err := anrCli.PauseNode(
|
|
context.Background(),
|
|
"sync",
|
|
)
|
|
gomega.Expect(err).To(gomega.BeNil())
|
|
|
|
awaitHealthy(anrCli, []instance{instances[0]})
|
|
|
|
ok, err := syncClient.Ping(context.Background())
|
|
gomega.Ω(ok).Should(gomega.BeFalse())
|
|
gomega.Ω(err).Should(gomega.HaveOccurred())
|
|
})
|
|
|
|
ginkgo.It("supports issuance of 256 more blocks", func() {
|
|
count += generateBlocks(context.Background(), count, 256, instances, true)
|
|
// TODO: verify all roots are equal
|
|
})
|
|
|
|
ginkgo.It("can re-sync the restarted node", func() {
|
|
_, err := anrCli.ResumeNode(
|
|
context.Background(),
|
|
"sync",
|
|
)
|
|
gomega.Expect(err).To(gomega.BeNil())
|
|
|
|
awaitHealthy(anrCli, []instance{instances[0]})
|
|
})
|
|
|
|
ginkgo.It("accepts transaction after restarted node state sync", func() {
|
|
acceptTransaction(syncClient, lsyncClient)
|
|
})
|
|
|
|
ginkgo.It("state sync while broadcasting transactions", func() {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
go func() {
|
|
// Recover failure if exits
|
|
defer ginkgo.GinkgoRecover()
|
|
|
|
count += generateBlocks(ctx, count, 0, instances, false)
|
|
}()
|
|
|
|
// Give time for transactions to start processing
|
|
time.Sleep(5 * time.Second)
|
|
|
|
// Start syncing node
|
|
cluster, err := anrCli.AddNode(
|
|
context.Background(),
|
|
"sync_concurrent",
|
|
execPath,
|
|
trackSubnetsOpt,
|
|
runner_sdk.WithChainConfigs(map[string]string{
|
|
blockchainID: vmConfig,
|
|
}),
|
|
)
|
|
gomega.Expect(err).To(gomega.BeNil())
|
|
awaitHealthy(anrCli, []instance{instances[0]})
|
|
|
|
nodeURI := cluster.ClusterInfo.NodeInfos["sync_concurrent"].Uri
|
|
uri := nodeURI + fmt.Sprintf("/ext/bc/%s", blockchainID)
|
|
bid, err := ids.FromString(blockchainID)
|
|
gomega.Expect(err).To(gomega.BeNil())
|
|
hutils.Outf("{{blue}}sync node uri: %s{{/}}\n", uri)
|
|
syncClient = rpc.NewJSONRPCClient(uri)
|
|
networkID, _, _, err := syncClient.Network(context.TODO())
|
|
gomega.Expect(err).To(gomega.BeNil())
|
|
lsyncClient = lrpc.NewJSONRPCClient(uri, networkID, bid)
|
|
cancel()
|
|
})
|
|
|
|
ginkgo.It("accepts transaction after state sync concurrent", func() {
|
|
acceptTransaction(syncClient, lsyncClient)
|
|
})
|
|
|
|
// TODO: restart all nodes (crisis simulation)
|
|
})
|
|
|
|
func awaitHealthy(cli runner_sdk.Client, instances []instance) {
|
|
for {
|
|
time.Sleep(healthPollInterval)
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
_, err := cli.Health(ctx)
|
|
cancel() // by default, health will wait to return until healthy
|
|
if err == nil {
|
|
return
|
|
}
|
|
hutils.Outf(
|
|
"{{yellow}}waiting for health check to pass...broadcasting tx while waiting:{{/}} %v\n",
|
|
err,
|
|
)
|
|
|
|
// Add more txs via other nodes until healthy (should eventually happen after
|
|
// [ValidityWindow] processed)
|
|
other, err := crypto.GeneratePrivateKey()
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
for _, instance := range instances {
|
|
parser, err := instance.lcli.Parser(context.Background())
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
submit, _, _, err := instance.cli.GenerateTransaction(
|
|
context.Background(),
|
|
parser,
|
|
nil,
|
|
&actions.Transfer{
|
|
To: other.PublicKey(),
|
|
Value: 1,
|
|
},
|
|
factory,
|
|
)
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
// Broadcast and wait for transaction
|
|
gomega.Ω(submit(context.Background())).Should(gomega.BeNil())
|
|
}
|
|
}
|
|
}
|
|
|
|
// generate blocks until either ctx is cancelled or the specified (!= 0) number of blocks is generated.
|
|
// if 0 blocks are specified, will just wait until ctx is cancelled.
|
|
func generateBlocks(
|
|
ctx context.Context,
|
|
cumulativeTxs int,
|
|
blocksToGenerate uint64,
|
|
instances []instance,
|
|
failOnError bool,
|
|
) int {
|
|
_, lastHeight, _, err := instances[0].cli.Accepted(context.Background())
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
parser, err := instances[0].lcli.Parser(context.Background())
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
var targetHeight uint64
|
|
if blocksToGenerate != 0 {
|
|
targetHeight = lastHeight + blocksToGenerate
|
|
}
|
|
for ctx.Err() == nil {
|
|
// Generate transaction
|
|
other, err := crypto.GeneratePrivateKey()
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
submit, _, _, err := instances[cumulativeTxs%len(instances)].cli.GenerateTransaction(
|
|
context.Background(),
|
|
parser,
|
|
nil,
|
|
&actions.Transfer{
|
|
To: other.PublicKey(),
|
|
Value: 1,
|
|
},
|
|
factory,
|
|
)
|
|
if failOnError {
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
} else if err != nil {
|
|
hutils.Outf(
|
|
"{{yellow}}unable to generate transaction:{{/}} %v\n",
|
|
err,
|
|
)
|
|
time.Sleep(5 * time.Second)
|
|
continue
|
|
}
|
|
|
|
// Broadcast transactions
|
|
err = submit(context.Background())
|
|
if failOnError {
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
} else if err != nil {
|
|
hutils.Outf(
|
|
"{{yellow}}tx broadcast failed:{{/}} %v\n",
|
|
err,
|
|
)
|
|
time.Sleep(5 * time.Second)
|
|
continue
|
|
}
|
|
cumulativeTxs++
|
|
_, height, _, err := instances[0].cli.Accepted(context.Background())
|
|
if failOnError {
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
} else if err != nil {
|
|
hutils.Outf(
|
|
"{{yellow}}height lookup failed:{{/}} %v\n",
|
|
err,
|
|
)
|
|
time.Sleep(5 * time.Second)
|
|
continue
|
|
}
|
|
if targetHeight != 0 && height > targetHeight {
|
|
break
|
|
} else if height > lastHeight {
|
|
lastHeight = height
|
|
hutils.Outf("{{yellow}}height=%d count=%d{{/}}\n", height, cumulativeTxs)
|
|
}
|
|
|
|
// Sleep for a very small amount of time to avoid overloading the
|
|
// network with transactions (can generate very fast)
|
|
time.Sleep(10 * time.Millisecond)
|
|
}
|
|
return cumulativeTxs
|
|
}
|
|
|
|
func acceptTransaction(cli *rpc.JSONRPCClient, lcli *lrpc.JSONRPCClient) {
|
|
parser, err := lcli.Parser(context.Background())
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
for {
|
|
// Generate transaction
|
|
other, err := crypto.GeneratePrivateKey()
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
submit, tx, _, err := cli.GenerateTransaction(
|
|
context.Background(),
|
|
parser,
|
|
nil,
|
|
&actions.Transfer{
|
|
To: other.PublicKey(),
|
|
Value: sendAmount,
|
|
},
|
|
factory,
|
|
)
|
|
gomega.Ω(err).Should(gomega.BeNil())
|
|
hutils.Outf("{{yellow}}generated transaction{{/}}\n")
|
|
|
|
// Broadcast and wait for transaction
|
|
gomega.Ω(submit(context.Background())).Should(gomega.BeNil())
|
|
hutils.Outf("{{yellow}}submitted transaction{{/}}\n")
|
|
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
|
|
success, err := lcli.WaitForTransaction(ctx, tx.ID())
|
|
cancel()
|
|
if err != nil {
|
|
hutils.Outf("{{red}}cannot find transaction: %v{{/}}\n", err)
|
|
continue
|
|
}
|
|
gomega.Ω(success).Should(gomega.BeTrue())
|
|
hutils.Outf("{{yellow}}found transaction{{/}}\n")
|
|
break
|
|
}
|
|
}
|