Verify BSP Node via API¶
This guide walks you through how to register your Backup Storage Provider (BSP) on-chain and verify it is eligible to participate in the DataHaven network through scripts that utilize the Polkadot API.
Prerequisites¶
- A running BSP node with your BSP secret/raw seed and your BSP's multiaddress handy.
- Node.js v22+ installed
-
A TypeScript project
Need a starter project?
If you don't have an existing project, follow these steps to create a TypeScript project you can use to follow the guides in this section:
-
Create a new project folder by executing the following command in the terminal:
-
Initialize a
package.jsonfile using the correct command for your package manager: -
Add the TypeScript and Node type definitions to your projects using the correct command for your package manager:
-
Create a
tsconfig.jsonfile in the root of your project and paste the following configuration: -
Initialize the
srcdirectory:
-
Deposit Requirements¶
The formula for the deposit is as follows:
SpMinDeposit + (capacity_in_gib * DepositPerData) + buffer
SpMinDeposit: Base deposit of 100 MOCKcapacity_in_gib: The set GiB capacity of your hardwareDepositPerData: 2 MOCK per GiBbuffer: An additional safety margin
Examples:
- 800 GiB capacity: 100 + (800 × 2) = 1,700 MOCK required (1,800 MOCK recommended)
- 1.6 TiB capacity: 100 + (1,638 × 2) = 3,376 MOCK required (3,500+ MOCK recommended)
The deposit is held (reserved) from your account when you start the BSP registration process and remains held while you operate as a BSP. The deposit is returned when you deregister as a BSP.
Note
Your BSP account must be funded before BSP registration.
Install Additional Dependencies¶
Install the following dependencies:
Set Up Keyrings¶
Set up the keyrings needed to sign BSP-related transactions and derive the correct on-chain identities.
-
Within your
servicesfolder, create a new file calledbspService.ts. -
Add the following code:
bspService.tsimport { ApiPromise, Keyring, WsProvider } from '@polkadot/api'; import { cryptoWaitReady } from '@polkadot/util-crypto'; import { types } from '@storagehub/types-bundle'; // Make sure WASM crypto is initialized *before* we create any keypairs await cryptoWaitReady(); // Create signer from secret URI const walletKeyring = new Keyring({ type: 'ethereum' }); const signer = walletKeyring.addFromUri('INSERT_PRIVATE_KEY'); // Make sure to use the BSP's seed phrase here // generated from following the "Run a BSP Node" steps const bspEvmKeyring = new Keyring({ type: 'ethereum' }); // BSP EVM signer const bspEvmSigner = bspEvmKeyring.addFromUri('INSERT_BSP_RAW_SEED'); const bspSubstrateKeyring = new Keyring({ type: 'ecdsa' }); // BSP Substrate signer const bspSubstrateSigner = bspSubstrateKeyring.addFromUri( 'INSERT_BSP_RAW_SEED' ); // If running BSP node on your local machine // create Polkadot API client, but for your actively running BSP node const localBSPwsUrl = `ws://127.0.0.1:9946`; const provider = new WsProvider(localBSPwsUrl); const polkadotApiBsp: ApiPromise = await ApiPromise.create({ provider, typesBundle: types, noInitWarn: true, }); export { signer, bspEvmSigner, bspSubstrateSigner, polkadotApiBsp };
Warning
It is assumed that private keys are securely stored and managed in accordance with standard security practices.
Request BSP Sign Up¶
Submit an on-chain request to register your node as an official Backup Storage Provider.
Add Helper Methods to Request Sign Up¶
Add reusable helper methods for funding your BSP account, checking balances, resolving multiaddresses, and submitting the sign-up transaction.
-
If you haven't already, create an
operationsfolder adjacent to yourservicesfolder: -
Create a
bspOperations.tsfile. -
Add the following helper methods to your
bspOperations.tsfile:bspOperations.tsimport { Binary } from 'polkadot-api'; import { bspEvmSigner, bspSubstrateSigner } from '../services/bspService.js'; import { chain, account, publicClient, walletClient, polkadotApi, } from '../services/clientService.js'; import { formatEther } from 'viem/utils'; import { polkadotApiBsp } from '../services/bspService.js'; export async function fundBspAddress( bspAddress: `0x${string}`, amount: bigint ) { console.log('Recipient:', bspAddress); console.log('Amount (raw):', amount.toString()); const txHash = await walletClient.sendTransaction({ account: account, chain: chain, to: bspAddress, value: amount, }); console.log('Tx hash:', txHash); const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash, }); console.log('Finalized in block:', receipt.blockNumber.toString()); } export async function checkBspBalance(bspAddress: `0x${string}`) { // Query balance const balance = formatEther( await publicClient.getBalance({ address: bspAddress }) ); console.log(`BSP EVM Address: ${bspAddress}`); console.log(`Balance: ${balance} MOCK`); } export async function getMultiaddresses() { try { const addresses = await polkadotApiBsp.rpc.system.localListenAddresses(); // addresses are Vec<Text> type. Turn into plain strings const stringAddresses = addresses .map((a) => a.toString()) .filter((addr) => !addr.includes('/ip4/127.0.0.1')) .filter((addr) => !addr.includes('/ip6/::1')); console.log('Local listen addresses:', stringAddresses); // Convert multiaddresses to Binary format return stringAddresses.map((addr) => Binary.fromText(addr)); } catch (error) { console.error('Error fetching local listen addresses:', error); throw new Error('Failed to fetch local listen addresses'); } } export async function requestBspSignUp( bspAddress: string, multiaddresses: Binary[], capacity: bigint ) { // Request BSP sign up const requestTx = polkadotApi.tx.Providers.request_bsp_sign_up({ capacity: capacity, multiaddresses: multiaddresses, payment_account: bspEvmSigner.address, // Account receiving payments }); // Sign and submit the request await new Promise<void>((resolve, reject) => { requestTx .signAndSend(bspSubstrateSigner, ({ status, dispatchError }) => { console.log('BSP sign-up requested. Waiting for finalization...'); if (dispatchError) { reject(dispatchError); } if (status.isFinalized) { console.log('Request finalized! Deposit has been reserved.'); resolve(); } }) .catch(reject); }); } // Add other BSP helper methods here
The roles each helper method plays:
fundBspAddress: Ensures the BSP account has enough balance to cover the required deposit.checkBspBalance: Verifies available funds of the BSP address.getMultiaddresses: Fetches the network addresses your BSP advertises to the network and selects the proper multiaddresses for therequest_bsp_sign_upextrinsic (needed only if you are running BSP node on your local machine).requestBspSignUp: Submits the on-chain request to register the BSP node by calling therequest_bsp_sign_upextrinsic.
Call the Request Sign Up Method¶
Trigger the BSP sign-up flow from your main script to submit the registration request on-chain.
-
Create an
index.tsfile adjacent to youroperationsandservicesfolder, if you haven't already. -
Add the following code:
index.tsimport { initWasm } from '@storagehub-sdk/core'; import { polkadotApi } from './services/clientService'; import { cancelBspSignUp, checkBspBalance, confirmBspSignUp, fundBspAddress, getMultiaddresses, requestBspSignUp, } from './operations/bspOperations'; import { bspEvmSigner } from './services/bspService'; async function runSignUpRequest() { // Initialize WASM await initWasm(); // Make sure to set the BSP public key from the seed phrase you saved when following the "Run a BSP Node" steps const bspAddress = bspEvmSigner.address as `0x${string}`; // console.log('bspSigner.address:', bspSigner.address); // Amount in smallest units of the native token. const depositAmount = 200_000_000_000_000_000_000n; await fundBspAddress(bspAddress, depositAmount); await checkBspBalance(bspAddress); // BSP configuration const multiaddresses = ['INSERT_MULTIADDRESS']; // If running BSP on your local machine // get multiaddresses from the BSP node directly by uncommenting the line below // const multiaddresses = await getMultiaddresses(); // Capacity should match the capacity set in docker-compose.yml // This is just an example value. const capacity = BigInt(10_737_418_240); // 10 GiB (80% of 12 GiB disk) await requestBspSignUp(bspAddress, multiaddresses, capacity); await polkadotApi.disconnect(); } // Add other script methods hereCheck the formula for the deposit to make sure you've prepared the right amount of funds.
Cancel BSP Sign Up If Needed¶
Mistakes can happen during the BSP verification process. That's why the cancel_sign_up extrinsic is available after the request_bsp_sign_up has been called in order to revoke the request before it has been finalized on-chain. This extrinsic can only be called prior to confirming verification via the confirm_sign_up extrinsic.
Add Helper Method to Cancel Sign Up¶
Add the cancelBspSignUp helper method that submits the transaction to cancel a pending BSP registration.
Add the following code to your bspOperations.ts file:
export async function cancelBspSignUp() {
// Cancel BSP sign-up
const cancelTx = polkadotApi.tx.Providers.cancel_sign_up({});
await new Promise<void>((resolve, reject) => {
cancelTx
.signAndSend(bspSubstrateSigner, ({ status, dispatchError }) => {
console.log('Cancelling BSP registration...');
if (dispatchError) {
reject(dispatchError);
}
if (status.isFinalized) {
console.log('BSP registration cancelled.');
resolve();
}
})
.catch(reject);
});
}
Call the Cancel Sign Up Method¶
Invoke the cancel method from your script to abort the BSP sign-up process, with the following code:
async function runSignUpCancel() {
// Initialize WASM
await initWasm();
// Registration can only be cancelled before the confirm_sign_up extrinsic is called
await cancelBspSignUp();
await polkadotApi.disconnect();
}
// Uncomment to run the cancel function
// runSignUpCancel();
Confirm BSP Sign Up¶
Confirm your BSP registration after the required waiting period has passed. You should only trigger the confirmBspSignUp method after a new epoch has begun. An epoch lasts 1 hour. You can check if a new epoch has begun on the Explorer page on Polkadot.js Apps.
Add Helper Method to Confirm Sign Up¶
To add the confirmBspSignUp helper method and thus finalize the BSP registration on-chain, add the following code to your bspOperations.ts file:
export async function confirmBspSignUp() {
// Confirm the sign-up (after waiting for randomness)
const confirmTx = polkadotApi.tx.Providers.confirm_sign_up({
provider_account: undefined, // Optional: omit to use signer's account
});
await new Promise<void>((resolve, reject) => {
confirmTx
.signAndSend(bspSubstrateSigner, ({ status, dispatchError }) => {
console.log('Confirming BSP registration...');
if (dispatchError) {
reject(dispatchError);
}
if (status.isFinalized) {
console.log('BSP registration confirmed and active!');
resolve();
}
})
.catch(reject);
});
}
Call the Confirm Sign Up Method¶
Call the confirmation method with the following code to complete BSP registration:
async function runSignUpConfirm() {
// Initialize WASM
await initWasm();
await confirmBspSignUp();
await polkadotApi.disconnect();
}
// Make sure to run `runSignUpRequest()` first and to
// wait accordingly before running `runSignUpConfirm()`
// as explained in the documentation
runSignUpRequest();
// runSignUpConfirm();
Next Steps¶
| Created: January 14, 2026