diff --git a/.gitignore b/.gitignore index 28be046..bb1bf59 100644 --- a/.gitignore +++ b/.gitignore @@ -74,4 +74,5 @@ compose-dev.yaml # Custom test_accounts/ *.pem -*.pub \ No newline at end of file +*.pub +*.key \ No newline at end of file diff --git a/Dockerfile.devnet b/Dockerfile.devnet index d040c89..880290d 100644 --- a/Dockerfile.devnet +++ b/Dockerfile.devnet @@ -11,6 +11,7 @@ RUN apt update && DEBIAN_FRONTEND=noninteractive apt install -y \ # Set working directory and Go mod cache path WORKDIR /app + ENV GOMODCACHE=/go/pkg/mod # Copy the avalanchego binary from the first stage @@ -40,5 +41,4 @@ RUN go mod download && go mod verify # Uncomment this if you have a main package to build. # RUN go build -o /app/nuklaivm ./cmd/nuklaivm -# Set entrypoint to a script that will stop and start the devnet -ENTRYPOINT ["bash", "-c", "./scripts/stop.sh; ./scripts/run.sh && echo 'Devnet started' && tail -f /dev/null"] +CMD ["bash", "-c", "./scripts/run.sh \"$INITIAL_OWNER_ADDRESS\" \"$EMISSION_ADDRESS\" && tail -f /dev/null"] diff --git a/README.md b/README.md index 7d7a620..318e9ab 100644 --- a/README.md +++ b/README.md @@ -149,12 +149,30 @@ This also allocates all funds on the network to `created address: 00c4cb545f748a key for this address is `323b1d8f4eed5f0da9da93071b034f2dce9d2d22692c172f3cb252a64ddfafd01b057de320297c29ad0c1f589ea216869cf1938d88c9fbd70d6748323dbf2fa7`. For convenience, this key has is also stored at `demo.pk`.\_ +Alternatively, you can also run the network via docker: + +```bash +./scripts/run_docker.sh start +``` + +And you can check out the logs at: + +```bash +./scripts/run_docker.sh logs +``` + To stop the network, run: -```sh +```bash ./scripts/stop.sh ``` +Alternatively, with docker: + +```bash +./scripts/run_docker.sh stop +``` + The run script uses AvalancheGo's [tmpnet](https://github.com/ava-labs/avalanchego/tree/master/tests/fixture/tmpnet) to launch a 2 node network with one node's server running at the hardcoded URI: `http://127.0.0.1:9650/ext/bc/nuklaivm`. Each default API comes with its own extension. For example, you can get the `networkID`, `subnetID`, and `chainID` by hitting the `/coreapi` extension: diff --git a/scripts/aws/deploy.devnet.sh b/scripts/aws/deploy.devnet.sh index c14a92c..fff136d 100755 --- a/scripts/aws/deploy.devnet.sh +++ b/scripts/aws/deploy.devnet.sh @@ -2,7 +2,6 @@ # Copyright (C) 2024, Nuklai. All rights reserved. # See the file LICENSE for licensing terms. - if [[ $(basename "$PWD") != "nuklaivm" ]]; then echo "Error: This script must be executed from the repository root (nuklaivm/)." exit 1 @@ -17,8 +16,13 @@ USER_DATA_FILE="./scripts/aws/install_docker.sh" TARBALL="nuklaivm.tar.gz" AMI_ID="ami-008d05461f83df5b1" +# Accept arguments or use default values +INITIAL_OWNER_ADDRESS=${1:-"002b5d019495996310f81c6a26a4dd9eeb9a3f3be1bac0a9294436713aecc84496"} +EMISSION_ADDRESS=${2:-"00f3b89e583e3944dee8d45ca40ce30829eff47481bc45669d401c2f9cc2bc110d"} + echo "Using AMI ID: $AMI_ID" +# Check if an instance is already running INSTANCE_ID=$(aws ec2 describe-instances --region $REGION \ --filters "Name=tag:Name,Values=$INSTANCE_NAME" "Name=instance-state-name,Values=running" \ --query "Reservations[0].Instances[0].InstanceId" --output text) @@ -30,6 +34,7 @@ if [ "$INSTANCE_ID" != "None" ]; then echo "Instance terminated." fi +# Launch a new EC2 instance echo "Launching a new EC2 instance..." INSTANCE_ID=$(aws ec2 run-instances --region $REGION \ --image-id $AMI_ID --count 1 --instance-type $INSTANCE_TYPE \ @@ -56,19 +61,25 @@ EXCLUDES=$(cat .gitignore .dockerignore 2>/dev/null | grep -v '^#' | sed '/^$/d' EXCLUDES+=" --exclude='./web_wallet' --exclude=$TARBALL" eval "tar -czf $TARBALL $EXCLUDES -C . ." -echo "Transferring tarball to the EC2 instance..." +echo "Transferring tarball and private key to the EC2 instance..." scp -o "StrictHostKeyChecking=no" -i $KEY_NAME $TARBALL ec2-user@$PUBLIC_IP:/home/ec2-user/ echo "Connecting to the EC2 instance and deploying devnet..." -ssh -o "StrictHostKeyChecking=no" -i $KEY_NAME ec2-user@$PUBLIC_IP << 'EOF' +ssh -o "StrictHostKeyChecking=no" -i $KEY_NAME ec2-user@$PUBLIC_IP << EOF docker stop nuklai-devnet || true docker rm nuklai-devnet || true cd /home/ec2-user tar -xzf nuklaivm.tar.gz --strip-components=1 + # Build the Docker image docker build -t nuklai-devnet -f Dockerfile.devnet . - docker run -d --name nuklai-devnet -p 9650:9650 nuklai-devnet + + # Run the Docker container with the passed arguments as environment variables + docker run -d --name nuklai-devnet -p 9650:9650 \ + -e INITIAL_OWNER_ADDRESS="$INITIAL_OWNER_ADDRESS" \ + -e EMISSION_ADDRESS="$EMISSION_ADDRESS" \ + nuklai-devnet echo "Devnet is running on the instance." EOF diff --git a/scripts/build.release.sh b/scripts/build.release.sh index 1f0bead..04a2917 100755 --- a/scripts/build.release.sh +++ b/scripts/build.release.sh @@ -6,12 +6,6 @@ set -o errexit set -o nounset set -o pipefail -# Set the CGO flags to use the portable version of BLST -# -# We use "export" here instead of just setting a bash variable because we need -# to pass this flag to all child processes spawned by the shell. -export CGO_CFLAGS="-O -D__BLST_PORTABLE__" CGO_ENABLED=1 - if ! [[ "$0" =~ scripts/build.release.sh ]]; then echo "must be run from nuklaivm root" exit 255 diff --git a/scripts/build.sh b/scripts/build.sh index d6098a6..4103feb 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -6,12 +6,6 @@ set -o errexit set -o nounset set -o pipefail -# Set the CGO flags to use the portable version of BLST -# -# We use "export" here instead of just setting a bash variable because we need -# to pass this flag to all child processes spawned by the shell. -export CGO_CFLAGS="-O -D__BLST_PORTABLE__" CGO_ENABLED=1 - # Get the directory of the script, even if sourced from another directory SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) diff --git a/scripts/deploy.devnet.sh b/scripts/deploy.devnet.sh index 372b4be..441a076 100755 --- a/scripts/deploy.devnet.sh +++ b/scripts/deploy.devnet.sh @@ -4,11 +4,8 @@ set -e -# Set the CGO flags to use the portable version of BLST -# -# We use "export" here instead of just setting a bash variable because we need -# to pass this flag to all child processes spawned by the shell. -export CGO_CFLAGS="-O -D__BLST_PORTABLE__" CGO_ENABLED=1 +source ./scripts/hypersdk/common/utils.sh +source ./scripts/hypersdk/constants.sh # Set console colors RED='\033[1;31m' diff --git a/scripts/hypersdk/constants.sh b/scripts/hypersdk/constants.sh index 8f50ae1..21c1854 100755 --- a/scripts/hypersdk/constants.sh +++ b/scripts/hypersdk/constants.sh @@ -7,4 +7,4 @@ # We use "export" here instead of just setting a bash variable because we need # to pass this flag to all child processes spawned by the shell. -export CGO_CFLAGS="-O -D__BLST_PORTABLE__" +export CGO_CFLAGS="-O -D__BLST_PORTABLE__" CGO_ENABLED=1 diff --git a/scripts/run.sh b/scripts/run.sh index d17295d..f5ce21e 100755 --- a/scripts/run.sh +++ b/scripts/run.sh @@ -4,11 +4,17 @@ set -e -# Set the CGO flags to use the portable version of BLST -# -# We use "export" here instead of just setting a bash variable because we need -# to pass this flag to all child processes spawned by the shell. -export CGO_CFLAGS="-O -D__BLST_PORTABLE__" CGO_ENABLED=1 +# Default values +DEFAULT_INITIAL_OWNER_ADDRESS="00c4cb545f748a28770042f893784ce85b107389004d6a0e0d6d7518eeae1292d9" +DEFAULT_EMISSION_ADDRESS="00c4cb545f748a28770042f893784ce85b107389004d6a0e0d6d7518eeae1292d9" + +# Read arguments from the command line, or use default values +INITIAL_OWNER_ADDRESS=${1:-$DEFAULT_INITIAL_OWNER_ADDRESS} +EMISSION_ADDRESS=${2:-$DEFAULT_EMISSION_ADDRESS} +# Shift arguments only if they are provided +[[ $# -ge 1 ]] && shift +[[ $# -ge 1 ]] && shift +# Remove these arguments from "$@" so they don’t go into additional_args # to run E2E tests (terminates cluster afterwards) # MODE=test ./scripts/run.sh @@ -23,6 +29,8 @@ source ./scripts/hypersdk/constants.sh VERSION=v1.11.12-rc.2 +echo "Running script with MODE=${MODE}" + ############################ # build avalanchego # https://github.com/ava-labs/avalanchego/releases @@ -75,16 +83,14 @@ go build \ ############################ echo "building e2e.test" - +rm -f ./tests/e2e/e2e.test prepare_ginkgo - -ACK_GINKGO_RC=true ginkgo build ./tests/e2e -./tests/e2e/e2e.test --help +ACK_GINKGO_RC=true ginkgo build ./tests/e2e || echo "ginkgo build failed" additional_args=("$@") if [[ ${MODE} == "run" ]]; then - echo "applying ginkgo.focus=Ping and --reuse-network to setup local network" + echo "applying --ginkgo.focus=Ping and --reuse-network to setup local network" additional_args+=("--ginkgo.focus=Ping") additional_args+=("--reuse-network") fi @@ -92,6 +98,8 @@ fi echo "running e2e tests" ./tests/e2e/e2e.test \ --ginkgo.v \ +--initial-owner-address="$INITIAL_OWNER_ADDRESS" \ +--emission-address="$EMISSION_ADDRESS" \ --avalanchego-path="${AVALANCHEGO_PATH}" \ --plugin-dir="${AVALANCHEGO_PLUGIN_DIR}" \ "${additional_args[@]}" diff --git a/scripts/run_docker.sh b/scripts/run_docker.sh new file mode 100755 index 0000000..e875ccc --- /dev/null +++ b/scripts/run_docker.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +# Copyright (C) 2024, Nuklai. All rights reserved. +# See the file LICENSE for licensing terms. + +set -e + +# Variables +IMAGE_NAME="nuklai-devnet" +CONTAINER_NAME="nuklai-devnet" + +# Function to stop and remove any existing container +cleanup_docker() { + echo "Stopping and removing any existing Docker container..." + docker stop "$CONTAINER_NAME" || true + docker rm "$CONTAINER_NAME" || true +} + +# Function to build the Docker image +build_docker_image() { + echo "Building the Docker image..." + docker build -t "$IMAGE_NAME" -f Dockerfile.devnet . +} + +# Function to start the Docker container with environment variables +start_container() { + # Use provided arguments, or default values if not set + local initial_owner_address=${1:-"00c4cb545f748a28770042f893784ce85b107389004d6a0e0d6d7518eeae1292d9"} + local emission_address=${2:-"00c4cb545f748a28770042f893784ce85b107389004d6a0e0d6d7518eeae1292d9"} + + echo "Starting the Docker container with passed arguments..." + docker run -d --name "$CONTAINER_NAME" \ + -p 9650:9650 \ + -e INITIAL_OWNER_ADDRESS="$initial_owner_address" \ + -e EMISSION_ADDRESS="$emission_address" \ + "$IMAGE_NAME" + echo "Docker container started. Use './scripts/run_docker.sh logs' to view logs." +} + +# Function to stop the Docker container +stop_container() { + echo "Stopping the Docker container..." + docker stop "$CONTAINER_NAME" || echo "No running container found." +} + +# Function to view logs of the container +view_logs() { + echo "Displaying logs for the container..." + docker logs -f "$CONTAINER_NAME" +} + +# Main logic to parse commands +case "$1" in + start) + cleanup_docker + build_docker_image + shift # Shift the first argument ("start") so the next ones are passed correctly + start_container "$@" # Pass remaining arguments to start_container + ;; + stop) + stop_container + ;; + logs) + view_logs + ;; + *) + echo "Usage: $0 {start [INITIAL_OWNER_ADDRESS] [EMISSION_ADDRESS]|stop|logs}" + exit 1 + ;; +esac \ No newline at end of file diff --git a/scripts/stop.sh b/scripts/stop.sh index 39ae35c..85862bc 100755 --- a/scripts/stop.sh +++ b/scripts/stop.sh @@ -4,9 +4,9 @@ set -e -VMWITHCONTRACTS_PATH=$( +NUKLAIVM_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" cd .. && pwd ) -ginkgo -v "$VMWITHCONTRACTS_PATH"/tests/e2e/e2e.test -- --stop-network +ginkgo -v "$NUKLAIVM_PATH"/tests/e2e/e2e.test -- --stop-network diff --git a/scripts/tests.benchmark.sh b/scripts/tests.benchmark.sh index 6618cd6..ed2eb64 100755 --- a/scripts/tests.benchmark.sh +++ b/scripts/tests.benchmark.sh @@ -4,12 +4,6 @@ set -e -# Set the CGO flags to use the portable version of BLST -# -# We use "export" here instead of just setting a bash variable because we need -# to pass this flag to all child processes spawned by the shell. -export CGO_CFLAGS="-O -D__BLST_PORTABLE__" CGO_ENABLED=1 - if ! [[ "$0" =~ scripts/tests.benchmark.sh ]]; then echo "must be run from nuklaivm root" exit 255 diff --git a/scripts/tests.integration.sh b/scripts/tests.integration.sh index 2705096..871ecee 100755 --- a/scripts/tests.integration.sh +++ b/scripts/tests.integration.sh @@ -4,12 +4,6 @@ set -e -# Set the CGO flags to use the portable version of BLST -# -# We use "export" here instead of just setting a bash variable because we need -# to pass this flag to all child processes spawned by the shell. -export CGO_CFLAGS="-O -D__BLST_PORTABLE__" CGO_ENABLED=1 - if ! [[ "$0" =~ scripts/tests.integration.sh ]]; then echo "must be run from nuklaivm root" exit 255 diff --git a/scripts/tests.unit.sh b/scripts/tests.unit.sh index cb3903d..b13e069 100755 --- a/scripts/tests.unit.sh +++ b/scripts/tests.unit.sh @@ -4,12 +4,6 @@ set -e -# Set the CGO flags to use the portable version of BLST -# -# We use "export" here instead of just setting a bash variable because we need -# to pass this flag to all child processes spawned by the shell. -export CGO_CFLAGS="-O -D__BLST_PORTABLE__" CGO_ENABLED=1 - if ! [[ "$0" =~ scripts/tests.unit.sh ]]; then echo "must be run from nuklaivm root" exit 255 diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index e6dae6e..dbd9d8c 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -6,6 +6,7 @@ package e2e_test import ( "encoding/base64" "encoding/json" + "flag" "fmt" "os" "path/filepath" @@ -28,21 +29,49 @@ import ( const owner = "nuklaivm-e2e-tests" -var flagVars *e2e.FlagVars +var ( + initialOwnerAddress string + emissionAddress string + flagVars *e2e.FlagVars +) func TestE2e(t *testing.T) { + // Parse all flags + flag.Parse() + + // Check if we're only stopping the network + stopNetwork := flag.Lookup("stop-network").Value.String() == "true" + if stopNetwork { + fmt.Println("Stopping the network...") + ginkgo.RunSpecs(t, "nuklaivm e2e test suites") + return + } + + // Validate that the required flags are provided if not stopping the network + if initialOwnerAddress == "" || emissionAddress == "" { + t.Fatalf("Missing required flags: --initial-owner-address or --emission-address") + } + + fmt.Printf("Initial Owner Address: %s\nEmission Address: %s\n", initialOwnerAddress, emissionAddress) + + // Run the full test suite ginkgo.RunSpecs(t, "nuklaivm e2e test suites") } func init() { + // Register existing e2e flags flagVars = e2e.RegisterFlags() + + // Register new flags for custom strings + flag.StringVar(&initialOwnerAddress, "initial-owner-address", "", "Initial Owner Address") + flag.StringVar(&emissionAddress, "emission-address", "", "Emission Address") } // Construct tmpnet network with a single VMWithContracts Subnet var _ = ginkgo.SynchronizedBeforeSuite(func() []byte { require := require.New(ginkgo.GinkgoT()) - gen, workloadFactory, err := workload.New(250 /* minBlockGap: 250ms */) + gen, workloadFactory, err := workload.New(250, initialOwnerAddress, emissionAddress) require.NoError(err) genesisBytes, err := json.Marshal(gen) diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go index 18ff477..b8ba524 100644 --- a/tests/integration/integration_test.go +++ b/tests/integration/integration_test.go @@ -25,7 +25,7 @@ func TestIntegration(t *testing.T) { var _ = ginkgo.BeforeSuite(func() { require := require.New(ginkgo.GinkgoT()) - genesis, workloadFactory, err := nuklaivmWorkload.New(0 /* minBlockGap: 0ms */) + genesis, workloadFactory, err := nuklaivmWorkload.New(0, "00c4cb545f748a28770042f893784ce85b107389004d6a0e0d6d7518eeae1292d9", "00c4cb545f748a28770042f893784ce85b107389004d6a0e0d6d7518eeae1292d9") require.NoError(err) genesisBytes, err := json.Marshal(genesis) diff --git a/tests/workload/workload.go b/tests/workload/workload.go index 55f0099..925244e 100644 --- a/tests/workload/workload.go +++ b/tests/workload/workload.go @@ -31,9 +31,9 @@ import ( ) const ( - initialBalance uint64 = 426_500_000_000_000_000 - maxSupply uint64 = 10_000_000_000_000_000_000 - txCheckInterval = 100 * time.Millisecond + initialOwnerBalance uint64 = 853_000_000_000_000_000 + maxSupply uint64 = 10_000_000_000_000_000_000 + txCheckInterval = 100 * time.Millisecond ) var ( @@ -67,18 +67,26 @@ type workloadFactory struct { addrs []codec.Address } -func New(minBlockGap int64) (*ngenesis.Genesis, workload.TxWorkloadFactory, error) { - customAllocs := make([]*genesis.CustomAllocation, 0, len(ed25519Addrs)) - for _, prefundedAddrStr := range ed25519Addrs { - customAllocs = append(customAllocs, &genesis.CustomAllocation{ - Address: prefundedAddrStr, - Balance: initialBalance, - }) +func New(minBlockGap int64, initialAddress, emissionAddress string) (*ngenesis.Genesis, workload.TxWorkloadFactory, error) { + var initialOwnerAddress codec.Address + if initialAddress == "" { + initialOwnerAddress = ed25519Addrs[0] + } else { + address, err := codec.StringToAddress(initialAddress) + if err != nil { + return nil, nil, err + } + initialOwnerAddress = address } + customAllocs := []*genesis.CustomAllocation{{Address: initialOwnerAddress, Balance: initialOwnerBalance}} + emissionBalancerAddress := ed25519Addrs[0].String() + if emissionAddress != "" { + emissionBalancerAddress = emissionAddress + } emissionBalancer := ngenesis.EmissionBalancer{ MaxSupply: maxSupply, - EmissionAddress: ed25519Addrs[0].String(), + EmissionAddress: emissionBalancerAddress, } genesis := ngenesis.NewGenesis(customAllocs, emissionBalancer) @@ -187,7 +195,7 @@ func (f *workloadFactory) NewWorkloads(uri string) ([]workload.TxWorkloadIterato {address: blsAddr, authFactory: blsFactory}, {address: secpAddr, authFactory: secpFactory}, }, - balance: initialBalance, + balance: initialOwnerBalance, cli: cli, lcli: lcli, networkID: networkID, diff --git a/vm/option.go b/vm/option.go index bf3e4eb..54d3d77 100644 --- a/vm/option.go +++ b/vm/option.go @@ -6,6 +6,7 @@ package vm import ( "github.com/nuklai/nuklaivm/emission" + "github.com/ava-labs/hypersdk/api/indexer" "github.com/ava-labs/hypersdk/vm" "github.com/ava-labs/hypersdk/x/contracts/runtime" ) @@ -32,6 +33,13 @@ func With() vm.Option { }) } +func WithIndexer() vm.Option { + return vm.NewOption(indexer.Namespace, indexer.Config{ + Enabled: true, + BlockWindow: 10000, + }, indexer.OptionFunc) +} + func WithRuntime() vm.Option { return vm.NewOption(Namespace+"runtime", *runtime.NewConfig(), func(v *vm.VM, cfg runtime.Config) error { wasmRuntime = runtime.NewRuntime(&cfg, v.Logger()) diff --git a/vm/vm.go b/vm/vm.go index 7964487..bcda6b9 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -11,7 +11,6 @@ import ( "github.com/nuklai/nuklaivm/genesis" "github.com/nuklai/nuklaivm/storage" - "github.com/ava-labs/hypersdk/api/indexer" "github.com/ava-labs/hypersdk/api/jsonrpc" "github.com/ava-labs/hypersdk/api/ws" "github.com/ava-labs/hypersdk/auth" @@ -103,7 +102,7 @@ func init() { // New returns a VM with the indexer, websocket, rpc, and external subscriber apis enabled. func New(options ...vm.Option) (*vm.VM, error) { opts := append([]vm.Option{ - indexer.With(), + WithIndexer(), ws.With(), jsonrpc.With(), With(), // Add Controller API