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

Improve the localhost script to also run a operator #22

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ To run tests for all packages:

### Localhost testing

To test the packages against a local node, you can use the script at `scripts/localhost.sh`.
To test the packages against a local node, you can use the script at `scripts/run-dev.js`.

1. Verify that the line 3-7 of the script matches your current OS and architecture.
1. Verify that the line 3-7 of the bash script in `scripts/download.sh` matches your current OS and architecture.

```bash
# Change the following variables as needed
Expand All @@ -64,10 +64,17 @@ To test the packages against a local node, you can use the script at `scripts/lo
2. Run the script:

```bash
./scripts/localhost.sh
node scripts/run-dev.js
```

This script will download the latest version of the node and farmer for your OS and architecture, start the node, and farmer
This script will:

- Download the latest version of the node and farmer for your OS and architecture (`scripts/download.sh`);
- Start the node, create and insert the keystore in the node (`scripts/run-node.sh`);
- Start the farmer (`scripts/run-farmer.sh`);
- Register the node as operator, wait and kill the node and farmer (inside `scripts/run-dev.js`);
- Start the node as an operator (`scripts/run-operator.sh`);
- Restart the farmer (`scripts/run-farmer.sh`).

3. Run the tests:

Expand Down
22 changes: 6 additions & 16 deletions scripts/localhost.sh → scripts/download.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,21 @@ ARCHITECTURE="aarch64" # aarch64 | x86_64-skylake | x86_64-v2

# GitHub repository
REPO="subspace/subspace"
TAG="latest" # "tags/gemini-3h-2024-may-06" # Tag of the release to download or "latest" for the latest release

# Directories
DOWNLOAD_DIR="downloads"
EXECUTABLE_DIR="executables"

# Delete the executable directory
rm -r "$EXECUTABLE_DIR"

# Create directories if they do not exist
mkdir -p "$DOWNLOAD_DIR"
mkdir -p "$EXECUTABLE_DIR"

# Get the latest release data
RELEASE_DATA=$(curl -s "https://api.github.com/repos/$REPO/releases/latest")
RELEASE_DATA=$(curl -s "https://api.github.com/repos/$REPO/releases/$TAG")

# Extract the download URLs for the selected os and architecture node and farmer assets
NODE_URL=$(echo $RELEASE_DATA | jq -r '.assets[] | select(.name | contains("subspace-node-'$OS'-'$ARCHITECTURE'")) | .browser_download_url')
Expand Down Expand Up @@ -50,18 +54,4 @@ chmod +x "$EXECUTABLE_DIR/farmer"
# Delete the downloads directory
rmdir "$DOWNLOAD_DIR"

echo "Downloaded and unzipped the latest node and farmer assets."

# Run node in the background
echo "Running node in the background..."
./executables/node run --dev --tmp --base-path executables/node-temp --farmer --name "localhost-node" --rpc-rate-limit 1000 --rpc-max-connections 10000 &

# Wait for 10 seconds before starting farmer
echo "Waiting for 10 seconds before starting farmer..."
sleep 10

# Run farmer
echo "Running farmer in the background..."
./executables/farmer farm --reward-address 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY path=executables/farmer-temp,size=1GiB &

echo "Both node and farmer are running in parallel."
echo "Downloaded and unzipped the latest node and farmer assets."
164 changes: 164 additions & 0 deletions scripts/run-dev.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
const { spawn } = require('node:child_process')
const { readdir, readFile } = require('node:fs/promises')
const { ApiPromise, Keyring, WsProvider } = require('@polkadot/api')
const { u8aToHex } = require('@polkadot/util')
const { createType } = require('@polkadot/types')

let runner = {
download: null,
node: null,
farmer: null,
}
let operatorRegistered = false

function downloadNodeAndFarmer() {
console.log('First lets download the node and farmer')
runner.download = spawn('bash', ['scripts/download.sh'], { detached: true })
runner.download.stdout.on('data', (data) => {
const message = data.toString()
console.log('\x1b[33m', 'Download: ', '\x1b[0m', message)
})
runner.download.stderr.on('data', (data) => {
const message = data.toString()
if (message.includes('%') || message.includes('--:--') || message.includes('Time Current'))
console.log('\x1b[33m', 'Download: ', '\x1b[0m', message)
else console.error('\x1b[31m', 'Download error: ', '\x1b[0m', message)
})
runner.download.on('close', (code) => {
console.log(`First script exited with code ${code}`)
if (code === 0) runSimpleNode()
else console.error('\x1b[31m', 'Download script failed with code: ', '\x1b[0m', code)
})
}

function runSimpleNode() {
console.log('Now lets start a simple node.')
runner.node = spawn('bash', ['scripts/run-node.sh'], { detached: true })
runner.node.stdout.on('data', (data) => {
const message = data.toString()
console.log('\x1b[36m', 'Node: ', '\x1b[0m', message)
if (runner.farmer === null && message.includes('Idle (0 peers)')) runFarmer()
})
runner.node.stderr.on('data', (data) =>
console.error('\x1b[31m', 'Node error: ', '\x1b[0m', data.toString()),
)
runner.node.on('close', (code) =>
console.log('\x1b[31m', 'Node exited with code: ', '\x1b[0m', code),
)
}

function runFarmer() {
console.log('Now lets start the farmer.')
runner.farmer = spawn('bash', ['scripts/run-farmer.sh'], { detached: true })
runner.farmer.stdout.on('data', (data) => {
const message = data.toString()
console.log('\x1b[35m', 'Farmer: ', '\x1b[0m', message)
if (!operatorRegistered && message.includes('Successfully signed reward hash')) {
operatorRegistered = true
registerOperator()
}
})
runner.farmer.stderr.on('data', (data) =>
console.error('\x1b[31m', 'Farmer error: ', '\x1b[0m', data.toString()),
)
runner.farmer.on('close', (code) =>
console.log('\x1b[31m', 'Farmer exited with code: ', '\x1b[0m', code),
)
}

function runOperatorNode() {
console.log('Now lets start a operator node.')
runner.operator = spawn('bash', ['scripts/run-operator.sh'])
runner.operator.stdout.on('data', (data) => {
const message = data.toString()
console.log('\x1b[32m', 'Operator: ', '\x1b[0m', message)
if (runner.farmer === null && message.includes('Idle (0 peers)')) runFarmer()
})
runner.operator.stderr.on('data', (data) =>
console.error('\x1b[31m', 'Operator error: ', '\x1b[0m', data.toString()),
)
runner.operator.on('close', (code) =>
console.log('\x1b[31m', 'Operator exited with code: ', '\x1b[0m', code),
)
}

async function registerOperator() {
console.log('Now lets register the operator.')

const keystorePath = 'executables/node-temp/domains/0/keystore/'
const files = await readdir(keystorePath)
if (files.length === 0) throw new Error('No files found in keystore directory.')

// Read the contents of the first file in the directory and extract and clean the seed
const seedFile = await readFile(`${keystorePath}/${files[0]}`, 'utf-8')
const seed = seedFile.trim().replace(/"/g, '')

// Create the provider and the API instance
const provider = new WsProvider('ws://127.0.0.1:9944/ws')
const api = await ApiPromise.create({ provider })

const AliceKeyring = new Keyring({ type: 'sr25519' })
const OperatorKeyring = new Keyring({ type: 'sr25519' })

const Alice = AliceKeyring.addFromUri('//Alice')
const Operator = OperatorKeyring.addFromUri(seed)

const signingKey = u8aToHex(Operator.publicKey)
const signature = Operator.sign(createType(api.registry, 'AccountId', Alice.address).toU8a())

const tx = await api.tx.domains.registerOperator(
'0',
'100000000000000000000',
{
signingKey,
minimumNominatorStake: '1000000000000000000',
nominationTax: '2',
},
signature,
)

await new Promise((resolve, reject) => {
tx.signAndSend(Alice, ({ status }) => {
if (status.isInBlock) {
txHash = status.asInBlock.toHex()
console.log(
'\x1b[33m',
'Registering operator: ',
'\x1b[0m',
'Successful transaction with tx.hash ' + txHash,
)
// Wait for 12 seconds before killing the node and farmer to make sure the operator is registered
setTimeout(() => {
console.log('\x1b[33m', 'Registering operator: ', '\x1b[0m', 'Killing node and farmer.')

process.kill(-runner.node.pid)
process.kill(-runner.farmer.pid)
runner.node = null
runner.farmer = null

console.log('\x1b[33m', 'Registering operator: ', '\x1b[0m', 'Node and farmer killed.')

// Wait for 2 seconds before starting the operator node
setTimeout(() => {
runner.node = runOperatorNode()
}, 5000)
}, 12000)

resolve()
} else if (
status.isRetracted ||
status.isFinalityTimeout ||
status.isDropped ||
status.isInvalid
) {
console.log('\x1b[31m', 'Registering operator: ', '\x1b[0m', 'Transaction failed')
reject(new Error('Transaction failed'))
} else
console.log('\x1b[33m', 'Registering operator: ', '\x1b[0m', 'Status of tx: ' + status.type)
})
})

await api.disconnect()
}

downloadNodeAndFarmer()
7 changes: 7 additions & 0 deletions scripts/run-farmer.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash

# Run farmer
echo "Running farmer..."
./executables/farmer farm path=executables/farmer-temp,size=1GiB --reward-address 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY --node-rpc-url ws://127.0.0.1:9944
marc-aurele-besner marked this conversation as resolved.
Show resolved Hide resolved

echo "Both node and farmer are running in parallel."
43 changes: 43 additions & 0 deletions scripts/run-node.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/bin/bash

# Set the base-path and domain-id variables
BASE_PATH="executables/node-temp"
DOMAIN_ID=0

# Color definitions
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

echo -e "${YELLOW}Create keystore...${NC}\n"

# Instructions for setting variables
echo -e "Using base-path: ${GREEN}$BASE_PATH${NC}"
echo -e "Using domain-id: ${GREEN}$DOMAIN_ID${NC}\n"
echo -e "${YELLOW}You can change these variables at the top of the script.${NC}"

# Create keystore
output=$(./executables/node domain key create --base-path "$BASE_PATH" --domain-id "$DOMAIN_ID")

# # Log the result of the first command
echo "$output"

# Extract the seed
seed=$(echo "$output" | grep "Seed:" | awk -F '"' '{print $2}')

# Check if seed was extracted
if [ -z "$seed" ]; then
echo -e "${RED}Failed to extract seed from the output.${NC}"
exit 1
fi

# Insert keystore with the extracted seed
echo -e "\n${YELLOW}Insert keystore...${NC}"
./executables/node domain key insert --base-path "$BASE_PATH" --domain-id "$DOMAIN_ID" --keystore-suri "$seed"
# Run node
echo -e "${GREEN}Keystore created successfully!${NC}"

# Run an node
echo "Running an node..."
./executables/node run --dev --farmer --timekeeper --base-path executables/node-temp --name "localhost-node" --rpc-rate-limit 1000 --rpc-max-connections 10000 --state-pruning archive-canonical --blocks-pruning 512 --rpc-cors all --force-synced --force-authoring
20 changes: 20 additions & 0 deletions scripts/run-operator.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/bash

# Set the base-path and domain-id variables
BASE_PATH="executables/node-temp"
DOMAIN_ID=0

# Color definitions
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# Instructions for setting variables
echo -e "Using base-path: ${GREEN}$BASE_PATH${NC}"
echo -e "Using domain-id: ${GREEN}$DOMAIN_ID${NC}\n"
echo -e "${YELLOW}You can change these variables at the top of the script.${NC}"

# Run an operator
echo "Running an operator..."
./executables/node run --dev --farmer --timekeeper --base-path "$BASE_PATH" --name "localhost-operator" --rpc-rate-limit 1000 --rpc-max-connections 10000 --state-pruning archive-canonical --blocks-pruning 512 --rpc-cors all --force-synced --force-authoring -- --domain-id 0 --operator-id 2 --state-pruning archive-canonical --blocks-pruning 512 --rpc-cors all
Loading