Skip to content

Commit a69e363

Browse files
committed
Added assign cores script and genesis config presets
1 parent 41a8dfb commit a69e363

File tree

6 files changed

+308
-6
lines changed

6 files changed

+308
-6
lines changed

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,29 @@ cargo build --release -p polkadot-bulletin-chain
220220
POLKADOT_BULLETIN_BINARY_PATH=./target/release/polkadot-bulletin-chain zombienet -p native spawn ./zombienet/bulletin-polkadot-local.toml
221221
```
222222

223+
### Run Bulletin Westend local chain
224+
225+
```bash
226+
# 1. Build and create the chain spec
227+
cargo build --release -p bulletin-westend-runtime
228+
./scripts/create_bulletin_westend_spec.sh
229+
230+
# 2. Spawn the zombienet
231+
POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \
232+
POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain \
233+
zombienet -p native spawn ./zombienet/bulletin-westend-local.toml
234+
```
235+
236+
#### Assigning Cores (Multi-Core Block Production)
237+
238+
After the zombienet is running, you need to assign cores to the bulletin parachain for block production. The relay chain is configured with 3 cores (`num_cores = 3`).
239+
240+
```bash
241+
./scripts/assign_cores.sh ws://localhost:9942 1006 0 1
242+
```
243+
244+
This uses sudo to submit a batch of `coretime.assignCore` extrinsics on the relay chain.
245+
223246
### Run a production chain (but only with Alice validator)
224247
You can override the Alice validator keys here: [adjust\_bp\_spec.sh](./zombienet/adjust_bp_spec.sh) (you should see finalized blocks in the logs).
225248

runtimes/bulletin-westend/src/lib.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,22 @@ use xcm_runtime_apis::{
8888
fees::Error as XcmPaymentApiError,
8989
};
9090

91+
/// Build with an offset of 1 behind the relay chain.
92+
const RELAY_PARENT_OFFSET: u32 = 1;
93+
94+
/// The upper limit of how many parachain blocks are processed by the relay chain per
95+
/// parent. Limits the number of blocks authored per slot. This determines the minimum
96+
/// block time of the parachain:
97+
/// `RELAY_CHAIN_SLOT_DURATION_MILLIS/BLOCK_PROCESSING_VELOCITY`
98+
const BLOCK_PROCESSING_VELOCITY: u32 = 3;
99+
100+
/// Maximum number of blocks simultaneously accepted by the Runtime, not yet included
101+
/// into the relay chain.
102+
const UNINCLUDED_SEGMENT_CAPACITY: u32 = (2 + RELAY_PARENT_OFFSET) * BLOCK_PROCESSING_VELOCITY + 1;
103+
104+
/// Relay chain slot duration, in milliseconds.
105+
const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000;
106+
91107
/// The address format for describing accounts.
92108
pub type Address = MultiAddress<AccountId, ()>;
93109

@@ -383,7 +399,7 @@ impl cumulus_pallet_parachain_system::Config for Runtime {
383399
type ReservedXcmpWeight = ReservedXcmpWeight;
384400
type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases;
385401
type ConsensusHook = ConsensusHook;
386-
type RelayParentOffset = ConstU32<0>;
402+
type RelayParentOffset = ConstU32<RELAY_PARENT_OFFSET>;
387403
}
388404

389405
type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook<
@@ -608,7 +624,7 @@ impl_runtime_apis! {
608624

609625
impl cumulus_primitives_core::RelayParentOffsetApi<Block> for Runtime {
610626
fn relay_parent_offset() -> u32 {
611-
0
627+
RELAY_PARENT_OFFSET
612628
}
613629
}
614630

scripts/assign_cores.sh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/bin/bash
2+
#
3+
# Assign extra cores to a parachain on the relay chain.
4+
#
5+
# Usage:
6+
# ./assign_cores.sh <relay_endpoint> <para_id> <cores...>
7+
#
8+
# Example:
9+
# ./assign_cores.sh ws://localhost:9942 1006 0 1 2
10+
11+
set -e
12+
13+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
14+
CMD_DIR="${SCRIPT_DIR}/assign_cores"
15+
16+
# Check if node is available
17+
if ! command -v node &> /dev/null; then
18+
echo "Error: Node.js is required but not installed."
19+
echo "Please install Node.js and try again."
20+
exit 1
21+
fi
22+
23+
# Check arguments
24+
if [ $# -lt 3 ]; then
25+
echo "Usage: $0 <relay_endpoint> <para_id> <cores...>"
26+
echo ""
27+
echo "Arguments:"
28+
echo " relay_endpoint WebSocket endpoint of the relay chain (e.g., ws://localhost:9942)"
29+
echo " para_id Parachain ID to assign cores to (e.g., 1006)"
30+
echo " cores Space-separated list of core numbers to assign"
31+
echo ""
32+
echo "Example:"
33+
echo " $0 ws://localhost:9942 1006 0 1 2"
34+
exit 1
35+
fi
36+
37+
# Install npm dependencies if node_modules doesn't exist
38+
if [ ! -d "${CMD_DIR}/node_modules" ]; then
39+
echo "Installing npm dependencies..."
40+
pushd "${CMD_DIR}" > /dev/null
41+
npm install
42+
popd > /dev/null
43+
echo ""
44+
fi
45+
46+
# Run the assign_cores script
47+
echo "Assigning cores to parachain..."
48+
node "${CMD_DIR}/assign_cores.js" "$@"
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/**
2+
* Script to assign extra cores to the bulletin parachain on the relay chain.
3+
*
4+
* This script constructs and submits a sudo(batch(assign_core)) extrinsic
5+
* to assign multiple cores to a specified parachain.
6+
*
7+
* Usage:
8+
* node assign_cores.js <relay_endpoint> <para_id> <cores...>
9+
*
10+
* Example:
11+
* node assign_cores.js ws://localhost:9942 1006 0 1 2
12+
* (assigns cores 0, 1, and 2 to parachain 1006)
13+
*/
14+
15+
const { ApiPromise, WsProvider, Keyring } = require("@polkadot/api");
16+
17+
async function connect(endpoint) {
18+
const provider = new WsProvider(endpoint);
19+
const api = await ApiPromise.create({
20+
provider,
21+
throwOnConnect: false,
22+
});
23+
return api;
24+
}
25+
26+
async function assignCores(endpoint, paraId, cores) {
27+
console.log(`Connecting to relay chain at: ${endpoint}`);
28+
console.log(`Assigning cores [${cores.join(", ")}] to parachain ${paraId}`);
29+
30+
const api = await connect(endpoint);
31+
32+
// Wait for the API to be ready
33+
await api.isReady;
34+
35+
// Create keyring and add Alice (used for sudo)
36+
const keyring = new Keyring({ type: "sr25519" });
37+
const alice = keyring.addFromUri("//Alice");
38+
39+
// Create assign_core calls for each core
40+
// Each assignment is: (CoreAssignment::Task(para_id), PartsOf57600)
41+
// 57600 represents a full timeslice allocation
42+
const assignCoreCalls = cores.map((core) => {
43+
return api.tx.coretime.assignCore(
44+
core, // core number
45+
0, // begin (immediate)
46+
[[{ Task: paraId }, 57600]], // assignment: [(Task(para_id), 57600)]
47+
null // end_hint: None
48+
);
49+
});
50+
51+
console.log(`Created ${assignCoreCalls.length} assign_core calls`);
52+
53+
// Wrap in utility.batch
54+
const batchCall = api.tx.utility.batch(assignCoreCalls);
55+
console.log("Created batch call");
56+
57+
// Wrap in sudo
58+
const sudoCall = api.tx.sudo.sudo(batchCall);
59+
console.log("Created sudo call");
60+
console.log(`Call data (hex): ${sudoCall.method.toHex()}`);
61+
62+
// Sign and submit the transaction
63+
console.log("Submitting transaction...");
64+
65+
return new Promise((resolve, reject) => {
66+
sudoCall.signAndSend(alice, { nonce: -1 }, ({ status, events, dispatchError }) => {
67+
console.log(`Transaction status: ${status.type}`);
68+
69+
if (status.isInBlock) {
70+
console.log(`Transaction included in block: ${status.asInBlock.toHex()}`);
71+
}
72+
73+
if (status.isFinalized) {
74+
console.log(`Transaction finalized in block: ${status.asFinalized.toHex()}`);
75+
76+
// Check for errors
77+
if (dispatchError) {
78+
if (dispatchError.isModule) {
79+
const decoded = api.registry.findMetaError(dispatchError.asModule);
80+
const { docs, name, section } = decoded;
81+
console.error(`Error: ${section}.${name}: ${docs.join(" ")}`);
82+
reject(new Error(`${section}.${name}`));
83+
} else {
84+
console.error(`Error: ${dispatchError.toString()}`);
85+
reject(new Error(dispatchError.toString()));
86+
}
87+
return;
88+
}
89+
90+
// Log events
91+
events.forEach(({ event }) => {
92+
const { section, method, data } = event;
93+
console.log(` Event: ${section}.${method}`, data.toString());
94+
});
95+
96+
// Check for sudo success
97+
const sudoSuccess = events.find(({ event }) =>
98+
event.section === "sudo" && event.method === "Sudid"
99+
);
100+
101+
if (sudoSuccess) {
102+
const result = sudoSuccess.event.data[0];
103+
if (result.isOk) {
104+
console.log("✅ Cores assigned successfully!");
105+
resolve();
106+
} else {
107+
console.error("❌ Sudo call failed:", result.asErr.toString());
108+
reject(new Error("Sudo call failed"));
109+
}
110+
} else {
111+
console.log("✅ Transaction finalized (no sudo event found, checking events above)");
112+
resolve();
113+
}
114+
115+
api.disconnect();
116+
}
117+
}).catch((err) => {
118+
console.error("Error submitting transaction:", err);
119+
reject(err);
120+
});
121+
});
122+
}
123+
124+
// Parse command line arguments
125+
const args = process.argv.slice(2);
126+
127+
if (args.length < 3) {
128+
console.log("Usage: node assign_cores.js <relay_endpoint> <para_id> <cores...>");
129+
console.log("");
130+
console.log("Arguments:");
131+
console.log(" relay_endpoint WebSocket endpoint of the relay chain (e.g., ws://localhost:9942)");
132+
console.log(" para_id Parachain ID to assign cores to (e.g., 1006)");
133+
console.log(" cores Space-separated list of core numbers to assign");
134+
console.log("");
135+
console.log("Example:");
136+
console.log(" node assign_cores.js ws://localhost:9942 1006 0 1 2");
137+
console.log(" (assigns cores 0, 1, and 2 to parachain 1006)");
138+
process.exit(1);
139+
}
140+
141+
const endpoint = args[0];
142+
const paraId = parseInt(args[1], 10);
143+
const cores = args.slice(2).map((c) => parseInt(c, 10));
144+
145+
if (isNaN(paraId)) {
146+
console.error("Error: para_id must be a number");
147+
process.exit(1);
148+
}
149+
150+
for (const core of cores) {
151+
if (isNaN(core)) {
152+
console.error("Error: all core numbers must be integers");
153+
process.exit(1);
154+
}
155+
}
156+
157+
assignCores(endpoint, paraId, cores)
158+
.then(() => {
159+
console.log("Done!");
160+
process.exit(0);
161+
})
162+
.catch((err) => {
163+
console.error("Failed to assign cores:", err.message);
164+
process.exit(1);
165+
});
166+

scripts/assign_cores/package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "bulletin-cmd",
3+
"version": "1.0.0",
4+
"description": "Command scripts for Bulletin Chain setup and management",
5+
"main": "assign_cores.js",
6+
"scripts": {
7+
"assign-cores": "node assign_cores.js"
8+
},
9+
"dependencies": {
10+
"@polkadot/api": "^16.5.2",
11+
"@polkadot/keyring": "^13.5.8",
12+
"@polkadot/util": "^13.5.8"
13+
}
14+
}
15+

zombienet/bulletin-westend-local.toml

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,34 @@
1-
# To run the network, execute the following command:
1+
# Bulletin Westend Local Network Configuration
22
#
3-
# cd <root dir>
4-
# ./scripts/create_bulletin_westend_spec.sh
5-
# POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain zombienet -p native spawn ./zombienet/bulletin-westend-local.toml
3+
# To run the network, execute the following commands:
4+
#
5+
# 1. Build and create the chain spec:
6+
# cd <root dir>
7+
# ./scripts/create_bulletin_westend_spec.sh
8+
#
9+
# 2. Spawn the zombienet:
10+
# POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \
11+
# POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain \
12+
# zombienet -p native spawn ./zombienet/bulletin-westend-local.toml
13+
#
14+
# 3. Assign cores to the bulletin parachain for block production:
15+
# After the network is spawned and running, assign cores to enable block production:
16+
#
17+
# ./scripts/assign_cores.sh ws://localhost:9942 1006 0 1 2 3
18+
#
19+
# This assigns cores 0, 1, 2, and 3 to parachain 1006 (bulletin).
20+
# Without explicit core assignment, the parachain may not produce blocks.
21+
#
22+
# The relay chain is configured with 4 cores (num_cores = 4) to support
23+
# multi-core parachain block production.
624

725
[settings]
826
node_spawn_timeout = 240
927

28+
[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params]
29+
max_validators_per_core = 1
30+
num_cores = 3
31+
1032
[relaychain]
1133
default_command = "{{POLKADOT_BINARY_PATH}}"
1234
default_args = ["-lruntime=debug,xcm=trace"]
@@ -28,6 +50,7 @@ balance = 2000000000000
2850
id = 1006
2951
chain_spec_path = "./zombienet/bulletin-westend-spec.json"
3052
cumulus_based = true
53+
default_args = ["--authoring", "slot-based"]
3154

3255
[[parachains.collators]]
3356
name = "bulletin-westend-collator-1"
@@ -50,3 +73,14 @@ args = [
5073
"--ipfs-server",
5174
"-lparachain=debug,runtime=trace,xcm=trace,bitswap=trace,sub-libp2p::bitswap=trace",
5275
]
76+
77+
[[parachains.collators]]
78+
name = "bulletin-westend-collator-3"
79+
command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}"
80+
validator = true
81+
p2p_port = 12347
82+
rpc_port = 12346
83+
args = [
84+
"--ipfs-server",
85+
"-lparachain=debug,runtime=trace,xcm=trace,bitswap=trace,sub-libp2p::bitswap=trace",
86+
]

0 commit comments

Comments
 (0)