Skip to content

Commit

Permalink
Merge pull request #22 from subspace/19-improve-the-localhost-script-…
Browse files Browse the repository at this point in the history
…to-also-run-a-operator

Improve the localhost script to also run a operator
  • Loading branch information
marc-aurele-besner authored Jun 11, 2024
2 parents 0252e96 + d551946 commit cc4ce35
Show file tree
Hide file tree
Showing 6 changed files with 251 additions and 20 deletions.
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=2GB --reward-address 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY --node-rpc-url ws://127.0.0.1:9944

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

0 comments on commit cc4ce35

Please sign in to comment.