Skip to content

Commit a4e46e4

Browse files
committed
refactor
1 parent 389f56c commit a4e46e4

File tree

1 file changed

+140
-90
lines changed

1 file changed

+140
-90
lines changed

examples/authorize_and_store_papi_smoldot.js

Lines changed: 140 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -12,118 +12,168 @@ import { Binary } from '@polkadot-api/substrate-bindings';
1212
// Generate PAPI descriptors using local node:
1313
// npx papi add -w ws://localhost:10000 bulletin
1414
// npx papi
15-
async function main() {
16-
await cryptoWaitReady();
17-
18-
// Bob's address - to get the chainspec
19-
console.log('Fetching chainspec from Bob node...');
20-
const bobWs = new WsProvider('ws://localhost:12346');
21-
const bobApi = await ApiPromise.create({ provider: bobWs });
22-
await bobApi.isReady;
23-
24-
// Create keyring and accounts
25-
const keyring = new Keyring({ type: 'sr25519' });
26-
const sudoAccount = keyring.addFromUri('//Alice');
27-
const whoAccount = keyring.addFromUri('//Alice');
28-
const sudoSigner = getPolkadotSigner(
29-
sudoAccount.publicKey,
30-
'Sr25519',
31-
(input) => sudoAccount.sign(input)
32-
);
33-
const whoSigner = getPolkadotSigner(
34-
whoAccount.publicKey,
35-
'Sr25519',
36-
(input) => whoAccount.sign(input)
37-
);
3815

39-
// Data
40-
const who = whoAccount.publicKey;
41-
const transactions = 32;
42-
const bytes = 64n * 1024n * 1024n; // 64 MB
16+
// Constants
17+
const BOB_NODE_WS = 'ws://localhost:12346';
18+
const ALICE_ADDRESS = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY";
19+
const SYNC_WAIT_MS = 15000;
20+
const SMOLDOT_LOG_LEVEL = 1; // 0=off, 1=error, 2=warn, 3=info, 4=debug, 5=trace
4321

44-
// Prepare data for storage
45-
const dataToStore = "Hello, Bulletin with PAPI + Smoldot - " + new Date().toString();
46-
const expectedCid = cidFromBytes(dataToStore);
22+
// Authorization parameters
23+
const AUTH_TRANSACTIONS = 32;
24+
const AUTH_BYTES = 64n * 1024n * 1024n; // 64 MB
4725

48-
// Note: In real usage, this step is not required — the chain spec with bootNodes should be included as part of the dApp.
49-
// For local testing, we use this to fetch the actual chain spec from the local node.
50-
// Get chain spec from Bob node and remove protocolId to allow smoldot to sync with local chain.
51-
// Use false to get full genesis spec, not light sync spec starting at finalized block
52-
const chainSpec = (await bobApi.rpc.syncstate.genSyncSpec(true)).toString();
26+
/**
27+
* Fetches and modifies the chain spec from a node for local testing
28+
* Note: In production, the chain spec with bootNodes should be bundled with the dApp
29+
*/
30+
async function fetchChainSpec(nodeWs) {
31+
console.log('Fetching chainspec from node...');
32+
const provider = new WsProvider(nodeWs);
33+
const api = await ApiPromise.create({ provider });
34+
await api.isReady;
35+
36+
const chainSpec = (await api.rpc.syncstate.genSyncSpec(true)).toString();
5337
const chainSpecObj = JSON.parse(chainSpec);
54-
chainSpecObj.protocolId = null;
55-
const modifiedChainSpec = JSON.stringify(chainSpecObj);
38+
chainSpecObj.protocolId = null; // Allow smoldot to sync with local chain
39+
40+
await api.disconnect();
41+
return JSON.stringify(chainSpecObj);
42+
}
5643

57-
// Initialize Smoldot client
44+
/**
45+
* Initializes Smoldot client with logging
46+
*/
47+
function initSmoldot() {
5848
const sd = smoldot.start({
59-
maxLogLevel: 4, // 0=off, 1=error, 2=warn, 3=info, 4=debug, 5=trace
49+
maxLogLevel: SMOLDOT_LOG_LEVEL,
6050
logCallback: (level, target, message) => {
6151
const levelNames = ['ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE'];
6252
const levelName = levelNames[level - 1] || 'UNKNOWN';
6353
console.log(`[smoldot:${levelName}] ${target}: ${message}`);
6454
}
6555
});
66-
const chain = await sd.addChain({ chainSpec: modifiedChainSpec });
67-
const client = createClient(getSmProvider(chain));
68-
const bulletinAPI = client.getTypedApi(bulletin);
56+
return sd;
57+
}
58+
59+
/**
60+
* Creates signers for accounts
61+
*/
62+
function createSigner(account) {
63+
return getPolkadotSigner(
64+
account.publicKey,
65+
'Sr25519',
66+
(input) => account.sign(input)
67+
);
68+
}
6969

70-
console.log('⏭️ Waiting for 15 seconds for smoldot to sync...');
71-
await new Promise(resolve => setTimeout(resolve, 15000));
70+
/**
71+
* Waits for a transaction to complete using observables
72+
*/
73+
function waitForTransaction(tx, signer, eventPrefix = "tx") {
74+
return new Promise((resolve, reject) => {
75+
tx.signSubmitAndWatch(signer).subscribe({
76+
next: (ev) => {
77+
console.log(`✅ ${eventPrefix} event:`, ev.type);
78+
if (ev.type === "txBestBlocksState" && ev.found) {
79+
console.log(`✅ ${eventPrefix} included in block:`, ev.block.hash);
80+
}
81+
},
82+
error: (err) => {
83+
console.error(`❌ ${eventPrefix} error:`, err);
84+
reject(err);
85+
},
86+
complete: () => {
87+
console.log(`✅ ${eventPrefix} complete!`);
88+
resolve();
89+
}
90+
});
91+
});
92+
}
7293

73-
const ALICE = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY";
94+
/**
95+
* Authorizes an account for transaction storage
96+
*/
97+
async function authorizeAccount(bulletinAPI, signer, who, transactions, bytes) {
98+
console.log('Authorizing account...');
7499
const authorizeTx = bulletinAPI.tx.TransactionStorage.authorize_account({
75-
who: ALICE,
100+
who,
76101
transactions,
77102
bytes
78103
});
79104
const sudoTx = bulletinAPI.tx.Sudo.sudo({
80105
call: authorizeTx.decodedCall
81106
});
82107

83-
sudoTx.signSubmitAndWatch(sudoSigner).subscribe({
84-
next: (ev) => {
85-
console.log("✅ Authorize event: ", ev.type)
86-
if (ev.type === "txBestBlocksState" && ev.found) {
87-
console.log("✅ Authorization included in block:", ev.block.hash)
88-
}
89-
},
90-
error: (err) => {
91-
console.error("❌ authorize error: ", err)
92-
client.destroy()
93-
sd.terminate()
94-
process.exit(1);
95-
},
96-
complete() {
97-
console.log("✅ Authorized! Now storing data...");
98-
99-
// Convert data to Uint8Array then wrap in Binary for PAPI typed API
100-
const dataBytes = new Uint8Array(Buffer.from(dataToStore));
101-
const binaryData = Binary.fromBytes(dataBytes);
102-
103-
bulletinAPI.tx.TransactionStorage.store({ data: binaryData })
104-
.signSubmitAndWatch(whoSigner).subscribe({
105-
next: (ev) => {
106-
console.log("⏭️ Store event: ", ev.type);
107-
if (ev.type === "txBestBlocksState" && ev.found) {
108-
console.log("✅ Data stored in block:", ev.block.hash);
109-
console.log("✅ Expected CID:", expectedCid);
110-
}
111-
},
112-
error: (err) => {
113-
console.error("❌ store error: ", err);
114-
client.destroy();
115-
sd.terminate();
116-
process.exit(1);
117-
},
118-
complete() {
119-
console.log("✅ Complete! Data stored successfully.");
120-
client.destroy();
121-
sd.terminate();
122-
process.exit(0);
123-
},
124-
});
125-
},
126-
});
108+
await waitForTransaction(sudoTx, signer, "Authorize");
109+
}
110+
111+
/**
112+
* Stores data to the bulletin chain
113+
*/
114+
async function storeData(bulletinAPI, signer, data) {
115+
console.log('Storing data...');
116+
const dataBytes = new Uint8Array(Buffer.from(data));
117+
const binaryData = Binary.fromBytes(dataBytes);
118+
119+
const storeTx = bulletinAPI.tx.TransactionStorage.store({ data: binaryData });
120+
await waitForTransaction(storeTx, signer, "Store");
121+
122+
const expectedCid = cidFromBytes(data);
123+
console.log("✅ Expected CID:", expectedCid);
124+
return expectedCid;
125+
}
126+
127+
async function main() {
128+
await cryptoWaitReady();
129+
130+
let sd, client;
131+
132+
try {
133+
// Setup keyring and accounts
134+
const keyring = new Keyring({ type: 'sr25519' });
135+
const sudoAccount = keyring.addFromUri('//Alice');
136+
const whoAccount = keyring.addFromUri('//Alice');
137+
const sudoSigner = createSigner(sudoAccount);
138+
const whoSigner = createSigner(whoAccount);
139+
140+
// Prepare data for storage
141+
const dataToStore = "Hello, Bulletin with PAPI + Smoldot - " + new Date().toString();
142+
143+
// Fetch chain spec and initialize Smoldot
144+
const chainSpec = await fetchChainSpec(BOB_NODE_WS);
145+
sd = initSmoldot();
146+
147+
const chain = await sd.addChain({ chainSpec });
148+
client = createClient(getSmProvider(chain));
149+
const bulletinAPI = client.getTypedApi(bulletin);
150+
151+
// Wait for smoldot to sync
152+
console.log(`⏭️ Waiting ${SYNC_WAIT_MS / 1000} seconds for smoldot to sync...`);
153+
await new Promise(resolve => setTimeout(resolve, SYNC_WAIT_MS));
154+
155+
// Execute authorization and storage sequentially
156+
await authorizeAccount(
157+
bulletinAPI,
158+
sudoSigner,
159+
ALICE_ADDRESS,
160+
AUTH_TRANSACTIONS,
161+
AUTH_BYTES
162+
);
163+
164+
await storeData(bulletinAPI, whoSigner, dataToStore);
165+
166+
console.log("✅ Data stored successfully.");
167+
168+
} catch (error) {
169+
console.error("❌ Error:", error);
170+
process.exit(1);
171+
} finally {
172+
// Cleanup
173+
if (client) client.destroy();
174+
if (sd) sd.terminate();
175+
process.exit(0);
176+
}
127177
}
128178

129179
await main();

0 commit comments

Comments
 (0)