Skip to content

Instantly share code, notes, and snippets.

@itzmeanjan
Last active May 11, 2021 06:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save itzmeanjan/9b8cdff401081e593f8a477ecba144f4 to your computer and use it in GitHub Desktop.
Save itzmeanjan/9b8cdff401081e593f8a477ecba144f4 to your computer and use it in GitHub Desktop.
Submit bulk Tx @ Matic DA Blockchain
const { ApiPromise, WsProvider, Keyring } = require('@polkadot/api')
const { Buffer } = require('buffer')
const { createWriteStream } = require('fs')
const BATCH = process.env.BATCH || 100 // these many tx(s) to be attempted to be submitted
const PAYLOAD = process.env.PAYLOAD || 512 // in terms of bytes
const WSURL = process.env.WSURL || 'ws://localhost:9944'
// To be initialized, after `setUp` is called
let handle
const fd = createWriteStream('./tx.log', { flags: 'a' })
const keyring = new Keyring({ type: 'sr25519' })
// Initializing Polkadot API for using it with Matic DA Blockchain
const setUp = async _ => {
const provider = new WsProvider(WSURL)
const api = await ApiPromise.create({
provider, types: {
ExtrinsicsRoot: {
hash: 'Hash',
commitment: 'Vec<u8>'
},
Header: {
parentHash: 'Hash',
number: 'Compact<BlockNumber>',
stateRoot: 'Hash',
extrinsicsRoot: 'ExtrinsicsRoot',
digest: 'Digest'
}
}
})
return api
}
// Generates random byte array of specified size
const generateData = size => {
let buffer = Buffer.alloc(size)
for (let i = 0; i < size; i++) {
buffer.writeUInt8(Math.floor(Math.random() * 256), i)
}
return buffer.toString('hex')
}
// Attempts to calculate nonce of account
const getNonce = async account => {
try {
const nonce = await handle.rpc.system.accountNextIndex(account.address)
return nonce
} catch (e) {
console.error(e.toString())
return 0
}
}
// Sends a tx & tracks its life cycle until it becomes finalized
const sendTx = (sender, nonce, size) => new Promise(async (res, rej) => {
try {
const unsub = await handle.tx.templateModule
.submitData(generateData(size))
.signAndSend(sender, { nonce }, async result => {
if (result.status.isReady) {
console.log(`Tx with nonce ${nonce} [ READY ]`)
return
}
// Wait until tx is included in block
if (result.status.isInBlock) {
console.log(`Tx with nonce ${nonce} in ${result.status.asInBlock} [ INCLUDED ]`)
unsub()
res(result.status.asInBlock)
}
})
} catch (e) {
rej(e)
}
})
// Sends `BATCH` many tx(s), where each attempts to submit `PAYLOAD` bytes
// data
const sendTxs = (sender, options) => new Promise(async (res, rej) => {
let batch = BATCH
let payload = PAYLOAD
if (options !== undefined && options !== null) {
batch = options.BATCH || BATCH
payload = options.PAYLOAD || PAYLOAD
}
let promises = []
let nonce = BigInt(await getNonce(sender))
for (let i = 0; i < batch; i++) {
promises.push(sendTx(sender, nonce, payload))
nonce += 1n // keep incrementing nonce before sending next tx
}
// Creating an easy to digest stat of how many tx(s) got included
// in each block
//
// @note We attempted to send `BATCH` tx(s) in a single go
// and may be not all of them got included in same block
await Promise.all(promises).then(v => {
res(v.reduce((acc, cur) => {
if (cur in acc) {
acc[cur]++
} else {
acc[cur] = 1
}
return acc
}, {}))
}).catch(rej)
})
// Main entry script, for submitting bulk tx(s)
const main = async _ => {
// Initialising global variable, so all tx sending can use this same API instance
handle = await setUp()
const alice = keyring.addFromUri('//Alice', { name: 'Alice default' })
const bob = keyring.addFromUri('//Bob', { name: 'Bob default' })
for (let batch = 1; batch < 10000; batch++) {
for (let payload = 1; payload < 2048; payload++) {
const options = { BATCH: batch, PAYLOAD: payload }
await Promise.all([sendTxs(alice, options), sendTxs(bob, options)]).then(v => {
const stat = {
...options,
stat: v.reduce((acc, cur) => {
Object.keys(cur).forEach(v => {
if (v in acc) {
acc[v] += cur[v]
} else {
acc[v] = cur[v]
}
})
return acc
}, {}),
...{ time: new Date().toUTCString() }
}
console.log(`✅ Tx(s) finalized : 👇`)
console.log(stat)
fd.write(`${JSON.stringify(stat, null, 2)}\n`)
}).catch(e => { console.error(e.toString()); fd.close(); process.exit(1) })
}
}
}
main().catch((e) => { console.error(e.toString()); process.exit(1); })
{
"name": "matic-da-bulk-tx",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "Anjan Roy<anjanroy@yandex.com>",
"license": "ISC",
"dependencies": {
"@polkadot/api": "^4.0.3"
}
}
@itzmeanjan
Copy link
Author

This script is supposed to be used for submitting bulk tx in Matic's DA blockchain.

Usage

  • Download GIST & install dependencies
npm i
  • Get a DA node up & running, with websocket interface exposed, by default ws://localhost:9944 to be used
  • Run index.js with
npm run start

It'll attempt to generate random byte array payload of size 512 bytes ( by default ) & submit 1024 ( by default ) tx(s) in a single go. After all those 1024 tx(s) get confirmed, it'll start next iteration.

If you want to change how many tx(s) to be sent/ how much data to be submitted in each tx, consider updating BATCH/ PAYLOAD, respectively.

@itzmeanjan
Copy link
Author

Updated script attempts to run for multiple possible combinations of BATCH_SIZE & PAYLOAD_SIZE & keeps result in tx.log file in JSON serialised form in append only mode.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment