Creating a Local Development Environment
Kurtosis has created an easy way to spin up a local NEAR testing environment using a Docker container.
This Kurtosis NEAR Package contains the following components:
- Indexer for Explorer
- NEAR Explorer
- NEAR Wallet
- Local RPC Endpoint
Visit here to see a short demo of the package in action.
Prerequisites
- Docker
- Yarn
- NEAR-CLI
- Kurtosis CLI
- Start Kurtosis engine after installation using:
kurtosis engine start
- Start Kurtosis engine after installation using:
Setup
Launch Kurtosis NEAR Package
Launch your Kurtosis NEAR Package in four easy steps!
-
Launch Docker
-
Copy the Kurtosis NEAR Package launch script by running the following:
curl -o ~/launch-local-near-cluster.sh https://raw.githubusercontent.com/kurtosis-tech/near-package/master/launch-local-near-cluster.sh -L
- Grant write permission to the script file you just downloaded:
chmod u+x ~/launch-local-near-cluster.sh
- Launch the Kurtosis NEAR Package:
If you're running the NEAR-in-Kurtosis cluster on your local machine:
~/launch-local-near-cluster.sh
If you're running your NEAR-in-Kurtosis cluster on a machine you intend to access remotely, replace 1.2.3.4
with the IP address of the machine you're running the cluster on:
~/launch-local-near-cluster.sh '{"backend_ip_address":"1.2.3.4"}'
Example response:
Created directory '/Users/zerix/.neartosis' for storing all NEAR-in-Kurtosis output
INFO[2022-12-06T12:59:04+05:30] Creating a new enclave for Starlark to run inside...
INFO[2022-12-06T12:59:14+05:30] Enclave 'near' created successfully
INFO[2022-12-06T12:59:14+05:30] Kurtosis CLI is running in a non interactive terminal. Everything will work but progress information and the progress bar will not be displayed.
> print "Starting the near-package with input struct(backend_ip_address = \"127.0.0.1\")"
Starting the near-package with input struct(backend_ip_address = "127.0.0.1")
> print "Launching contract helper postgresql"
Launching contract helper postgresql
> print "Adding contract helper Posgresql DB running on port '5432'"
Adding contract helper Posgresql DB running on port '5432'
> add_service service_id="contract-helper-db"
Service 'contract-helper-db' added with service GUID 'contract-helper-db-1670311755'
> exec service_id="contract-helper-db" command=["sleep", "10"]
Command returned with exit code '0' with no output
> exec service_id="contract-helper-db" command=["psql", "-U", "near", "-c", "\\l"]
Command returned with exit code '0' and the following output:
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------+-------+----------+------------+------------+-------------------
near | near | UTF8 | en_US.utf8 | en_US.utf8 |
postgres | near | UTF8 | en_US.utf8 | en_US.utf8 |
template0 | near | UTF8 | en_US.utf8 | en_US.utf8 | =c/near +
| | | | | near=CTc/near
template1 | near | UTF8 | en_US.utf8 | en_US.utf8 | =c/near +
| | | | | near=CTc/near
(4 rows)
> exec service_id="contract-helper-db" command=["psql", "-U", "near", "-c", "create database indexer with owner=near"]
Command returned with exit code '0' and the following output: 'CREATE DATABASE'
> exec service_id="contract-helper-db" command=["psql", "-U", "near", "-c", "create database analytics with owner=near"]
Command returned with exit code '0' and the following output: 'CREATE DATABASE'
> exec service_id="contract-helper-db" command=["psql", "-U", "near", "-c", "create database telemetry with owner=near"]
Command returned with exit code '0' and the following output: 'CREATE DATABASE'
> print "Contract helper postgresql db info struct(analytics_db = \"analytics\", db_user_password = \"near\", db_username = \"near\", indexer_db = \"indexer\", private_url = struct(ip_address = \"contract-helper-db\", path = \"\", port_number = 5432, protocol = \"postgres\"), telemetry_db = \"telemetry\")"
Contract helper postgresql db info struct(analytics_db = "analytics", db_user_password = "near", db_username = "near", indexer_db = "indexer", private_url = struct(ip_address = "contract-helper-db", path = "", port_number = 5432, protocol = "postgres"), telemetry_db = "telemetry")
> print "Launching contract helper dynamo db"
Launching contract helper dynamo db
> print "Adding contract helper DynamoDB running on default port '8000'"
Adding contract helper DynamoDB running on default port '8000'
> add_service service_id="contract-helper-dynamo-db"
Service 'contract-helper-dynamo-db' added with service GUID 'contract-helper-dynamo-db-1670311770'
> print "Contract helper dynamodb info struct(private_url = struct(ip_address = \"contract-helper-dynamo-db\", path = \"\", port_number = 8000, protocol = \"TCP\"))"
Contract helper dynamodb info struct(private_url = struct(ip_address = "contract-helper-dynamo-db", path = "", port_number = 8000, protocol = "TCP"))
> print "Launching indexer"
Launching indexer
> print "Adding indexer service..."
Adding indexer service...
> upload_files src="github.com/kurtosis-tech/near-package/static_files/near-configs/localnet" artifact_id="8f5279c5-d012-4543-88e7-e9829b6d6040"
Files uploaded with artifact ID '8f5279c5-d012-4543-88e7-e9829b6d6040'
> add_service service_id="indexer-node"
Service 'indexer-node' added with service GUID 'indexer-node-1670311774'
> exec service_id="indexer-node" command=["sleep", "10"]
Command returned with exit code '0' with no output
> exec service_id="indexer-node" command=["cat", "/root/.near/validator_key.json"]
Command returned with exit code '0' and the following output:
{
"account_id": "test.near",
"public_key": "ed25519:3Kuyi2DUXdoHgoaNEvCxa1m6G8xqc6Xs7WGajaqLhNmW",
"secret_key": "ed25519:2ykcMLiM7vCmsSECcgfmUzihBtNdBv7v2CxNi94sNt4R8ar4xsrMMYvtsSNGQDfSRhNWXEnZvgx2wzS9ViBiS9jW"
}
> print "Indexer launched with struct(private_rpc_url = struct(ip_address = \"indexer-node\", path = \"\", port_number = 3030, protocol = \"http\"), public_rpc_url = struct(ip_address = \"127.0.0.1\", path = \"\", port_number = 8332, protocol = \"http\"), validator_key = {\"account_id\": \"test.near\", \"public_key\": \"ed25519:3Kuyi2DUXdoHgoaNEvCxa1m6G8xqc6Xs7WGajaqLhNmW\", \"secret_key\": \"ed25519:2ykcMLiM7vCmsSECcgfmUzihBtNdBv7v2CxNi94sNt4R8ar4xsrMMYvtsSNGQDfSRhNWXEnZvgx2wzS9ViBiS9jW\"})"
Indexer launched with struct(private_rpc_url = struct(ip_address = "indexer-node", path = "", port_number = 3030, protocol = "http"), public_rpc_url = struct(ip_address = "127.0.0.1", path = "", port_number = 8332, protocol = "http"), validator_key = {"account_id": "test.near", "public_key": "ed25519:3Kuyi2DUXdoHgoaNEvCxa1m6G8xqc6Xs7WGajaqLhNmW", "secret_key": "ed25519:2ykcMLiM7vCmsSECcgfmUzihBtNdBv7v2CxNi94sNt4R8ar4xsrMMYvtsSNGQDfSRhNWXEnZvgx2wzS9ViBiS9jW"})
> print "Launching contract helper"
Launching contract helper
> print "Adding contract helper service running on port '3000'"
Adding contract helper service running on port '3000'
> add_service service_id="contract-helper-service"
Service 'contract-helper-service' added with service GUID 'contract-helper-service-1670311796'
> print "Contract helper launchded with struct(private_url = struct(ip_address = \"contract-helper-service\", path = \"\", port_number = 3000, protocol = \"http\"), public_url = struct(ip_address = \"127.0.0.1\", path = \"\", port_number = 8330, protocol = \"http\"))"
Contract helper launchded with struct(private_url = struct(ip_address = "contract-helper-service", path = "", port_number = 3000, protocol = "http"), public_url = struct(ip_address = "127.0.0.1", path = "", port_number = 8330, protocol = "http"))
> print "Launching explorer backend"
Launching explorer backend
> print "Adding explorer backend service"
Adding explorer backend service
> add_service service_id="explorer-backend"
Service 'explorer-backend' added with service GUID 'explorer-backend-1670311799'
> print "Explorer backend launchded with struct(private_url = struct(ip_address = \"explorer-backend\", path = \"\", port_number = 8080, protocol = \"http\"), public_url = struct(ip_address = \"127.0.0.1\", path = \"\", port_number = 18080, protocol = \"http\"))"
Explorer backend launchded with struct(private_url = struct(ip_address = "explorer-backend", path = "", port_number = 8080, protocol = "http"), public_url = struct(ip_address = "127.0.0.1", path = "", port_number = 18080, protocol = "http"))
> print "Launching explorer frontend"
Launching explorer frontend
> print "Adding explorer frontend service running on port '3000'"
Adding explorer frontend service running on port '3000'
> add_service service_id="explorer-frontend"
Service 'explorer-frontend' added with service GUID 'explorer-frontend-1670311803'
> print "Explorer frontend launchded with struct(public_url = struct(ip_address = \"127.0.0.1\", path = \"\", port_number = 8331, protocol = \"http\"))"
Explorer frontend launchded with struct(public_url = struct(ip_address = "127.0.0.1", path = "", port_number = 8331, protocol = "http"))
> print "Launching wallet"
Launching wallet
> print "Adding wallet service running on port '3004"
Adding wallet service running on port '3004
> print "Replacing variable 'NODE_URL' to 'http://127.0.0.1:8332' using regexp: '([,{])NODE_URL:[^,]*([,}])'"
Replacing variable 'NODE_URL' to 'http://127.0.0.1:8332' using regexp: '([,{])NODE_URL:[^,]*([,}])'
> print "Replacing variable 'ACCOUNT_HELPER_URL' to 'http://127.0.0.1:8330' using regexp: '([,{])ACCOUNT_HELPER_URL:[^,]*([,}])'"
Replacing variable 'ACCOUNT_HELPER_URL' to 'http://127.0.0.1:8330' using regexp: '([,{])ACCOUNT_HELPER_URL:[^,]*([,}])'
> print "Replacing variable 'EXPLORER_URL' to 'http://127.0.0.1:8331' using regexp: '([,{])EXPLORER_URL:[^,]*([,}])'"
Replacing variable 'EXPLORER_URL' to 'http://127.0.0.1:8331' using regexp: '([,{])EXPLORER_URL:[^,]*([,}])'
> print "Replacing variable 'NETWORK_ID' to 'localnet' using regexp: '([,{])NETWORK_ID:[^,]*([,}])'"
Replacing variable 'NETWORK_ID' to 'localnet' using regexp: '([,{])NETWORK_ID:[^,]*([,}])'
> print "Replacing variable 'ACCOUNT_ID_SUFFIX' to 'test.near' using regexp: '([,{])ACCOUNT_ID_SUFFIX:[^,]*([,}])'"
Replacing variable 'ACCOUNT_ID_SUFFIX' to 'test.near' using regexp: '([,{])ACCOUNT_ID_SUFFIX:[^,]*([,}])'
> print "Replacing variable 'ACCESS_KEY_FUNDING_AMOUNT' to '3000000000000000000000000' using regexp: '([,{])ACCESS_KEY_FUNDING_AMOUNT:[^,]*([,}])'"
Replacing variable 'ACCESS_KEY_FUNDING_AMOUNT' to '3000000000000000000000000' using regexp: '([,{])ACCESS_KEY_FUNDING_AMOUNT:[^,]*([,}])'
> add_service service_id="wallet"
Service 'wallet' added with service GUID 'wallet-1670311807'
> print "Explorer wallet struct(public_url = struct(ip_address = \"127.0.0.1\", path = \"\", port_number = 8334, protocol = \"http\"))"
Explorer wallet struct(public_url = struct(ip_address = "127.0.0.1", path = "", port_number = 8334, protocol = "http"))
Starlark code successfully run. Output was:
{
"contract_helper_service_url": "http://127.0.0.1:8330",
"explorer_url": "http://127.0.0.1:8331",
"near_node_rpc_url": "http://127.0.0.1:8332",
"network_name": "localnet",
"root_validator_key": {
"account_id": "test.near",
"public_key": "ed25519:3Kuyi2DUXdoHgoaNEvCxa1m6G8xqc6Xs7WGajaqLhNmW",
"secret_key": "ed25519:2ykcMLiM7vCmsSECcgfmUzihBtNdBv7v2CxNi94sNt4R8ar4xsrMMYvtsSNGQDfSRhNWXEnZvgx2wzS9ViBiS9jW"
},
"wallet_url": "http://127.0.0.1:8334"
}
INFO[2022-12-06T13:00:10+05:30] =============================================
INFO[2022-12-06T13:00:10+05:30] || Created enclave: near ||
INFO[2022-12-06T13:00:10+05:30] =============================================
============================================================ SUCCESS ================================================================================
ACTION Paste the following in your terminal to declare the following variables so you can use them:
export NEAR_ENV="local"
export NEAR_CLI_LOCALNET_NETWORK_ID="localnet"
export NEAR_NODE_URL="http://127.0.0.1:8332"
export NEAR_CLI_LOCALNET_KEY_PATH="/Users/zerix/.neartosis/2022-12-06T12.59.04/validator-key.json"
export NEAR_WALLET_URL="http://127.0.0.1:8334"
export NEAR_HELPER_URL="http://127.0.0.1:8330"
export NEAR_HELPER_ACCOUNT="test.near
\"ed25519:2ykcMLiM7vCmsSECcgfmUzihBtNdBv7v2CxNi94sNt4R8ar4xsrMMYvtsSNGQDfSRhNWXEnZvgx2wzS9ViBiS9jW\"})
ed25519:2ykcMLiM7vCmsSECcgfmUzihBtNdBv7v2CxNi94sNt4R8ar4xsrMMYvtsSNGQDfSRhNWXEnZvgx2wzS9ViBiS9jW"})
test.near"
export NEAR_EXPLORER_URL="http://127.0.0.1:8331"
ACTION Paste the following into your terminal now to use the 'local_near' command as a replacement for the NEAR CLI for connecting to your
local cluster (e.g. 'local_near login'):
alias local_near='NEAR_ENV="local" NEAR_CLI_LOCALNET_NETWORK_ID="localnet" NEAR_NODE_URL="http://127.0.0.1:8332" NEAR_CLI_LOCALNET_KEY_PATH="/Users/zerix/.neartosis/2022-12-06T12.59.04/validator-key.json" NEAR_WALLET_URL="http://127.0.0.1:8334" NEAR_HELPER_URL="http://127.0.0.1:8330" NEAR_HELPER_ACCOUNT="test.near
\"ed25519:2ykcMLiM7vCmsSECcgfmUzihBtNdBv7v2CxNi94sNt4R8ar4xsrMMYvtsSNGQDfSRhNWXEnZvgx2wzS9ViBiS9jW\"})
ed25519:2ykcMLiM7vCmsSECcgfmUzihBtNdBv7v2CxNi94sNt4R8ar4xsrMMYvtsSNGQDfSRhNWXEnZvgx2wzS9ViBiS9jW"})
test.near" NEAR_EXPLORER_URL="http://127.0.0.1:8331" near'
ACTION If you want the 'local_near' command available in all your new terminal windows, add the above alias into your .bash_profile/.bashrc/.zshrc
file and open a new terminal window.
ACTION To stop your cluster, run the following:
kurtosis enclave stop near
ACTION To remove your cluster, run:
kurtosis clean -a
============================================================ SUCCESS ================================================================================
The URLs and validator key value above will be the same for each run of Kurtosis, so you can safely use these values in your config files.
If you're running Kurtosis on a remote machine, you'll also need to:
- Replace the
127.0.0.1
IP addresses in the environment variables with the IP address of your remote machine - Copy the validator key from where it lives on the machine running Kurtosis (in
NEAR_CLI_LOCALNET_KEY_PATH
) to somewhere on your local machine - Adjust the value of the
NEAR_CLI_LOCALNET_KEY_PATH
on your local machine to match the location you stored the key at
If you ever forget the above URLs, you can inspect the cluster:
kurtosis enclave inspect near
Setup Environment Variables
After deploying your Kurtosis NEAR Package, you will need to setup some environment variables to make life a lot easier. Notice the ACTION sections in your terminal log from the package deployment. You will be using these exact values to setup these variables.
- Follow the first ACTION item from the deployment log by copying all of the export commands and running them in your terminal.
Example exports: (DO NOT COPY ~ yours will be slightly different)
export NEAR_ENV="local"
export NEAR_CLI_LOCALNET_NETWORK_ID="localnet"
export NEAR_NODE_URL="http://127.0.0.1:8332"
export NEAR_CLI_LOCALNET_KEY_PATH="/Users/zerix/.neartosis/2022-06-03T18.04.32/validator-key.json"
export NEAR_WALLET_URL="http://127.0.0.1:8334"
export NEAR_HELPER_URL="http://127.0.0.1:8330"
export NEAR_HELPER_ACCOUNT="test.near"
export NEAR_EXPLORER_URL="http://127.0.0.1:8331"
- Proceed to the second ACTION item which asks you to create an alias for
local_near
. This is what we will use when runningnear-cli
commands with our test environment.
Example alias: (DO NOT COPY ~ yours will be slightly different)
alias local_near='NEAR_ENV="local" NEAR_CLI_LOCALNET_NETWORK_ID="localnet" NEAR_NODE_URL="http://127.0.0.1:8332" NEAR_CLI_LOCALNET_KEY_PATH="/Users/zerix/.neartosis/2022-06-03T18.04.32/validator-key.json" NEAR_WALLET_URL="http://127.0.0.1:8334" NEAR_HELPER_URL="http://127.0.0.1:8330" NEAR_HELPER_ACCOUNT="test.near" NEAR_EXPLORER_URL="http://127.0.0.1:8331" near'
Now replacing near
with local_near
when running near-cli
commands will perform these actions in your local test environment.
Testing
Ensure that your alias is working correctly by checking the state of the root account test.near
.
Run the following in your terminal:
local_near state test.near
This should return something similar to the following output:
Loaded master account test.near key from /Users/zerix/.neartosis/2022-06-03T18.04.32/validator-key.json with public key = ed25519:3Kuyi2DUXdoHgoaNEvCxa1m6G8xqc6Xs7WGajaqLhNmW
Account test.near
{
amount: '1000000000000000000000000000000000',
block_hash: 'G8jx4pYgqFSFSCDyM9MvVYj3HAdgRuxhkAHGweNhUNrY',
block_height: 224,
code_hash: '11111111111111111111111111111111',
locked: '50000000000000000000000000000000',
storage_paid_at: 0,
storage_usage: 182,
formattedAmount: '1,000,000,000'
}
Congratulations! Setup is complete and you are ready to start exploring your local NEAR blockchain! 🎉
The Kurtosis Team has created a great video presentation that covers the above steps as well as demoing the functionality of this local network setup.