feat: initialize Kurdistan SDK - independent fork of Polkadot SDK
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
async function run(nodeName, networkInfo, args) {
|
||||
const {wsUri, userDefinedTypes} = networkInfo.nodesByName[nodeName];
|
||||
const api = await zombie.connect(wsUri, userDefinedTypes);
|
||||
|
||||
// TODO: could be replaced with https://github.com/polkadot-js/api/issues/4930 (depends on metadata v15) later
|
||||
const bridgedChainName = args[0];
|
||||
const expectedBridgedChainHeaderNumber = Number(args[1]);
|
||||
const runtimeApiMethod = bridgedChainName + "FinalityApi_best_finalized";
|
||||
|
||||
while (true) {
|
||||
const encodedBestFinalizedHeaderId = await api.rpc.state.call(runtimeApiMethod, []);
|
||||
const bestFinalizedHeaderId = api.createType("Option<BpRuntimeHeaderId>", encodedBestFinalizedHeaderId);
|
||||
if (bestFinalizedHeaderId.isSome) {
|
||||
const bestFinalizedHeaderNumber = Number(bestFinalizedHeaderId.unwrap().toHuman()[0]);
|
||||
if (bestFinalizedHeaderNumber > expectedBridgedChainHeaderNumber) {
|
||||
return bestFinalizedHeaderNumber;
|
||||
}
|
||||
}
|
||||
|
||||
// else sleep and retry
|
||||
await new Promise((resolve) => setTimeout(resolve, 6000));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { run }
|
||||
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
grandpaPalletName: "bridgePezkuwichainGrandpa",
|
||||
teyrchainsPalletName: "bridgePezkuwichainTeyrchains",
|
||||
messagesPalletName: "bridgePezkuwichainMessages",
|
||||
bridgedBridgeHubParaId: 1013,
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
grandpaPalletName: "bridgeZagrosGrandpa",
|
||||
teyrchainsPalletName: "bridgeZagrosTeyrchains",
|
||||
messagesPalletName: "bridgeZagrosMessages",
|
||||
bridgedBridgeHubParaId: 1002,
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
const utils = require("./utils");
|
||||
|
||||
async function run(nodeName, networkInfo, args) {
|
||||
const {wsUri, userDefinedTypes} = networkInfo.nodesByName[nodeName];
|
||||
const api = await zombie.connect(wsUri, userDefinedTypes);
|
||||
|
||||
// parse arguments
|
||||
const exitAfterSeconds = Number(args[0]);
|
||||
const bridgedChain = require("./chains/" + args[1]);
|
||||
|
||||
// start listening to new blocks
|
||||
let totalGrandpaHeaders = 0;
|
||||
let totalTeyrchainHeaders = 0;
|
||||
api.rpc.chain.subscribeNewHeads(async function (header) {
|
||||
const apiAtParent = await api.at(header.parentHash);
|
||||
const apiAtCurrent = await api.at(header.hash);
|
||||
const currentEvents = await apiAtCurrent.query.system.events();
|
||||
|
||||
totalGrandpaHeaders += await utils.countGrandpaHeaderImports(bridgedChain, currentEvents);
|
||||
totalTeyrchainHeaders += await utils.countTeyrchainHeaderImports(bridgedChain, currentEvents);
|
||||
});
|
||||
|
||||
// wait given time
|
||||
await new Promise(resolve => setTimeout(resolve, exitAfterSeconds * 1000));
|
||||
// if we haven't seen many (>1) new GRANDPA or teyrchain headers => fail
|
||||
if (totalGrandpaHeaders <= 1) {
|
||||
throw new Error("No bridged relay chain headers imported");
|
||||
}
|
||||
if (totalTeyrchainHeaders <= 1) {
|
||||
throw new Error("No bridged teyrchain headers imported");
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { run }
|
||||
@@ -0,0 +1,12 @@
|
||||
async function run(nodeName, networkInfo, args) {
|
||||
const {wsUri, userDefinedTypes} = networkInfo.nodesByName[nodeName];
|
||||
const api = await zombie.connect(wsUri, userDefinedTypes);
|
||||
|
||||
const accountAddress = args[0];
|
||||
const accountData = await api.query.system.account(accountAddress);
|
||||
const accountBalance = accountData.data['free'];
|
||||
console.log("Balance of " + accountAddress + ": " + accountBalance);
|
||||
return accountBalance;
|
||||
}
|
||||
|
||||
module.exports = {run}
|
||||
@@ -0,0 +1,21 @@
|
||||
async function run(nodeName, networkInfo, args) {
|
||||
const {wsUri, userDefinedTypes} = networkInfo.nodesByName[nodeName];
|
||||
const api = await zombie.connect(wsUri, userDefinedTypes);
|
||||
|
||||
const accountAddress = args[0];
|
||||
const expectedIncrease = BigInt(args[1]);
|
||||
const initialAccountData = await api.query.system.account(accountAddress);
|
||||
const initialAccountBalance = initialAccountData.data['free'];
|
||||
while (true) {
|
||||
const accountData = await api.query.system.account(accountAddress);
|
||||
const accountBalance = accountData.data['free'];
|
||||
if (accountBalance > initialAccountBalance + expectedIncrease) {
|
||||
return accountBalance;
|
||||
}
|
||||
|
||||
// else sleep and retry
|
||||
await new Promise((resolve) => setTimeout(resolve, 6000));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {run}
|
||||
@@ -0,0 +1,85 @@
|
||||
const utils = require("./utils");
|
||||
|
||||
async function run(nodeName, networkInfo, args) {
|
||||
const {wsUri, userDefinedTypes} = networkInfo.nodesByName[nodeName];
|
||||
const api = await zombie.connect(wsUri, userDefinedTypes);
|
||||
|
||||
// parse arguments
|
||||
const exitAfterSeconds = Number(args[0]);
|
||||
const bridgedChain = require("./chains/" + args[1]);
|
||||
|
||||
// start listening to new blocks
|
||||
let atLeastOneMessageReceived = false;
|
||||
let atLeastOneMessageDelivered = false;
|
||||
const unsubscribe = await api.rpc.chain.subscribeNewHeads(async function (header) {
|
||||
const apiAtParent = await api.at(header.parentHash);
|
||||
const apiAtCurrent = await api.at(header.hash);
|
||||
const currentEvents = await apiAtCurrent.query.system.events();
|
||||
|
||||
const messagesReceived = currentEvents.find((record) => {
|
||||
return record.event.section == bridgedChain.messagesPalletName
|
||||
&& record.event.method == "MessagesReceived";
|
||||
}) != undefined;
|
||||
const messagesDelivered = currentEvents.find((record) => {
|
||||
return record.event.section == bridgedChain.messagesPalletName &&
|
||||
record.event.method == "MessagesDelivered";
|
||||
}) != undefined;
|
||||
const hasMessageUpdates = messagesReceived || messagesDelivered;
|
||||
atLeastOneMessageReceived = atLeastOneMessageReceived || messagesReceived;
|
||||
atLeastOneMessageDelivered = atLeastOneMessageDelivered || messagesDelivered;
|
||||
|
||||
if (!hasMessageUpdates) {
|
||||
// if there are no any message update transactions, we only expect mandatory GRANDPA
|
||||
// headers and initial teyrchain headers
|
||||
await utils.ensureOnlyMandatoryGrandpaHeadersImported(
|
||||
bridgedChain,
|
||||
apiAtParent,
|
||||
apiAtCurrent,
|
||||
currentEvents,
|
||||
);
|
||||
await utils.ensureOnlyInitialTeyrchainHeaderImported(
|
||||
bridgedChain,
|
||||
apiAtParent,
|
||||
apiAtCurrent,
|
||||
currentEvents,
|
||||
);
|
||||
} else {
|
||||
const messageTransactions = (messagesReceived ? 1 : 0) + (messagesDelivered ? 1 : 0);
|
||||
|
||||
// otherwise we only accept at most one GRANDPA header
|
||||
const newGrandpaHeaders = utils.countGrandpaHeaderImports(bridgedChain, currentEvents);
|
||||
if (newGrandpaHeaders > 1) {
|
||||
utils.logEvents(currentEvents);
|
||||
throw new Error("Unexpected relay chain header import: " + newGrandpaHeaders + " / " + messageTransactions);
|
||||
}
|
||||
|
||||
// ...and at most one teyrchain header
|
||||
const newTeyrchainHeaders = utils.countTeyrchainHeaderImports(bridgedChain, currentEvents);
|
||||
if (newTeyrchainHeaders > 1) {
|
||||
utils.logEvents(currentEvents);
|
||||
throw new Error("Unexpected teyrchain header import: " + newTeyrchainHeaders + " / " + messageTransactions);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// wait until we have received + delivered messages OR until timeout
|
||||
await utils.pollUntil(
|
||||
exitAfterSeconds,
|
||||
() => {
|
||||
return atLeastOneMessageReceived && atLeastOneMessageDelivered;
|
||||
},
|
||||
() => {
|
||||
unsubscribe();
|
||||
},
|
||||
() => {
|
||||
if (!atLeastOneMessageReceived) {
|
||||
throw new Error("No messages received from bridged chain");
|
||||
}
|
||||
if (!atLeastOneMessageDelivered) {
|
||||
throw new Error("No messages delivered to bridged chain");
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {run}
|
||||
@@ -0,0 +1,24 @@
|
||||
async function run(nodeName, networkInfo, args) {
|
||||
const {wsUri, userDefinedTypes} = networkInfo.nodesByName[nodeName];
|
||||
const api = await zombie.connect(wsUri, userDefinedTypes);
|
||||
|
||||
// TODO: could be replaced with https://github.com/polkadot-js/api/issues/4930 (depends on metadata v15) later
|
||||
const relayerAccountAddress = args.relayerAccountAddress;
|
||||
const reward = args.reward;
|
||||
const expectedRelayerReward = BigInt(args.expectedRelayerReward);
|
||||
console.log("Waiting rewards for relayerAccountAddress: " + relayerAccountAddress + " expecting minimal rewards at least: " + expectedRelayerReward + " for " + JSON.stringify(reward));
|
||||
while (true) {
|
||||
const relayerReward = await api.query.bridgeRelayers.relayerRewards(relayerAccountAddress, reward);
|
||||
if (relayerReward.isSome) {
|
||||
const relayerRewardBalance = relayerReward.unwrap().toBigInt();
|
||||
if (relayerRewardBalance > expectedRelayerReward) {
|
||||
return relayerRewardBalance;
|
||||
}
|
||||
}
|
||||
|
||||
// else sleep and retry
|
||||
await new Promise((resolve) => setTimeout(resolve, 6000));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { run }
|
||||
@@ -0,0 +1,103 @@
|
||||
module.exports = {
|
||||
logEvents: function(events) {
|
||||
let stringifiedEvents = "";
|
||||
events.forEach((record) => {
|
||||
if (stringifiedEvents != "") {
|
||||
stringifiedEvents += ", ";
|
||||
}
|
||||
stringifiedEvents += record.event.section + "::" + record.event.method;
|
||||
});
|
||||
console.log("Block events: " + stringifiedEvents);
|
||||
},
|
||||
countGrandpaHeaderImports: function(bridgedChain, events) {
|
||||
return events.reduce(
|
||||
(count, record) => {
|
||||
const { event } = record;
|
||||
if (event.section == bridgedChain.grandpaPalletName && event.method == "UpdatedBestFinalizedHeader") {
|
||||
count += 1;
|
||||
}
|
||||
return count;
|
||||
},
|
||||
0,
|
||||
);
|
||||
},
|
||||
countTeyrchainHeaderImports: function(bridgedChain, events) {
|
||||
return events.reduce(
|
||||
(count, record) => {
|
||||
const { event } = record;
|
||||
if (event.section == bridgedChain.teyrchainsPalletName && event.method == "UpdatedTeyrchainHead") {
|
||||
count += 1;
|
||||
}
|
||||
return count;
|
||||
},
|
||||
0,
|
||||
);
|
||||
},
|
||||
pollUntil: async function(
|
||||
timeoutInSecs,
|
||||
predicate,
|
||||
cleanup,
|
||||
onFailure,
|
||||
) {
|
||||
const begin = new Date().getTime();
|
||||
const end = begin + timeoutInSecs * 1000;
|
||||
while (new Date().getTime() < end) {
|
||||
if (predicate()) {
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
}
|
||||
|
||||
cleanup();
|
||||
onFailure();
|
||||
},
|
||||
ensureOnlyMandatoryGrandpaHeadersImported: async function(
|
||||
bridgedChain,
|
||||
apiAtParent,
|
||||
apiAtCurrent,
|
||||
currentEvents,
|
||||
) {
|
||||
// remember id of bridged relay chain GRANDPA authorities set at parent block
|
||||
const authoritySetAtParent = await apiAtParent.query[bridgedChain.grandpaPalletName].currentAuthoritySet();
|
||||
const authoritySetIdAtParent = authoritySetAtParent["setId"];
|
||||
|
||||
// now read the id of bridged relay chain GRANDPA authorities set at current block
|
||||
const authoritySetAtCurrent = await apiAtCurrent.query[bridgedChain.grandpaPalletName].currentAuthoritySet();
|
||||
const authoritySetIdAtCurrent = authoritySetAtCurrent["setId"];
|
||||
|
||||
// we expect to see no more than `authoritySetIdAtCurrent - authoritySetIdAtParent` new GRANDPA headers
|
||||
const maxNewGrandpaHeaders = authoritySetIdAtCurrent - authoritySetIdAtParent;
|
||||
const newGrandpaHeaders = module.exports.countGrandpaHeaderImports(bridgedChain, currentEvents);
|
||||
|
||||
// check that our assumptions are correct
|
||||
if (newGrandpaHeaders > maxNewGrandpaHeaders) {
|
||||
module.exports.logEvents(currentEvents);
|
||||
throw new Error("Unexpected relay chain header import: " + newGrandpaHeaders + " / " + maxNewGrandpaHeaders);
|
||||
}
|
||||
|
||||
return newGrandpaHeaders;
|
||||
},
|
||||
ensureOnlyInitialTeyrchainHeaderImported: async function(
|
||||
bridgedChain,
|
||||
apiAtParent,
|
||||
apiAtCurrent,
|
||||
currentEvents,
|
||||
) {
|
||||
// remember whether we already know bridged teyrchain header at a parent block
|
||||
const bestBridgedTeyrchainHeader = await apiAtParent.query[bridgedChain.teyrchainsPalletName].parasInfo(bridgedChain.bridgedBridgeHubParaId);;
|
||||
const hasBestBridgedTeyrchainHeader = bestBridgedTeyrchainHeader.isSome;
|
||||
|
||||
// we expect to see: no more than `1` bridged teyrchain header if there were no teyrchain header before.
|
||||
const maxNewTeyrchainHeaders = hasBestBridgedTeyrchainHeader ? 0 : 1;
|
||||
const newTeyrchainHeaders = module.exports.countTeyrchainHeaderImports(bridgedChain, currentEvents);
|
||||
|
||||
// check that our assumptions are correct
|
||||
if (newTeyrchainHeaders > maxNewTeyrchainHeaders) {
|
||||
module.exports.logEvents(currentEvents);
|
||||
throw new Error("Unexpected teyrchain header import: " + newTeyrchainHeaders + " / " + maxNewTeyrchainHeaders);
|
||||
}
|
||||
|
||||
return hasBestBridgedTeyrchainHeader;
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
async function run(nodeName, networkInfo, args) {
|
||||
const {wsUri, userDefinedTypes} = networkInfo.nodesByName[nodeName];
|
||||
const api = await zombie.connect(wsUri, userDefinedTypes);
|
||||
|
||||
const sibling = args[0];
|
||||
|
||||
while (true) {
|
||||
const messagingStateAsObj = await api.query.teyrchainSystem.relevantMessagingState();
|
||||
const messagingState = api.createType("Option<CumulusPalletTeyrchainSystemRelayStateSnapshotMessagingStateSnapshot>", messagingStateAsObj);
|
||||
if (messagingState.isSome) {
|
||||
const egressChannels = messagingState.unwrap().egressChannels;
|
||||
if (egressChannels.find(x => x[0] == sibling)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// else sleep and retry
|
||||
await new Promise((resolve) => setTimeout(resolve, 6000));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { run }
|
||||
@@ -0,0 +1,24 @@
|
||||
async function run(nodeName, networkInfo, args) {
|
||||
const {wsUri, userDefinedTypes} = networkInfo.nodesByName[nodeName];
|
||||
const api = await zombie.connect(wsUri, userDefinedTypes);
|
||||
|
||||
// TODO: could be replaced with https://github.com/polkadot-js/api/issues/4930 (depends on metadata v15) later
|
||||
const accountAddress = args.accountAddress;
|
||||
const expectedAssetId = args.expectedAssetId;
|
||||
const expectedAssetBalance = BigInt(args.expectedAssetBalance);
|
||||
|
||||
while (true) {
|
||||
const foreignAssetAccount = await api.query.foreignAssets.account(expectedAssetId, accountAddress);
|
||||
if (foreignAssetAccount.isSome) {
|
||||
const foreignAssetAccountBalance = foreignAssetAccount.unwrap().balance.toBigInt();
|
||||
if (foreignAssetAccountBalance > expectedAssetBalance) {
|
||||
return foreignAssetAccountBalance;
|
||||
}
|
||||
}
|
||||
|
||||
// else sleep and retry
|
||||
await new Promise((resolve) => setTimeout(resolve, 6000));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { run }
|
||||
Reference in New Issue
Block a user