Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Additional bridges+zombienet test: only required bridged headers are synced #2982

Closed
wants to merge 68 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
5cb9a57
additional bridges+zombienet tests
svyatonik Jan 17, 2024
2578aae
need to do init first
svyatonik Jan 18, 2024
582f9c7
add new CI job: zombienet-bridges-0002-mandatory-headers-synced-while…
svyatonik Jan 18, 2024
9f82ec0
meaningful names for logs
svyatonik Jan 18, 2024
f86cc23
remove test file
svyatonik Jan 18, 2024
cfdac5c
remove IFS
svyatonik Jan 18, 2024
dda1aaa
start relayer directly from tests in test-1
svyatonik Jan 18, 2024
e3948c2
more tracing to debug 10m timeout
svyatonik Jan 18, 2024
8b6e5f8
where are asset-hub-westend-collator logs?
svyatonik Jan 18, 2024
692182f
trigger CI
svyatonik Jan 18, 2024
17ce70a
invoker logs
svyatonik Jan 18, 2024
606ab38
Merge branch 'master' into sv-more-bridges-zombienet-tests
svyatonik Jan 18, 2024
9095ff6
use --noWait for multiple transactions
svyatonik Jan 19, 2024
a7c4886
increase timeouts to account bridge initialization
svyatonik Jan 19, 2024
da0d3c2
please start pipeline
svyatonik Jan 19, 2024
3580101
increase timeout again
svyatonik Jan 19, 2024
cb2bf32
new tets + rearrange steps in test 1
svyatonik Jan 19, 2024
ab5adce
remove test lines
svyatonik Jan 19, 2024
71fffea
also rearrange steps in 3rd test
svyatonik Jan 19, 2024
a9bd8fa
add test 3 to CI
svyatonik Jan 19, 2024
0f1c1b0
lost change in polkadot-js-api call :/
svyatonik Jan 19, 2024
05affc5
it is not supported by latests release :(
svyatonik Jan 19, 2024
c4cdde1
then we need to increase timeouts :(
svyatonik Jan 19, 2024
737de80
-debug
svyatonik Jan 19, 2024
de995e6
revert
svyatonik Jan 19, 2024
5ce37af
small fixes
svyatonik Jan 22, 2024
fbe9f2c
Merge branch 'master' into sv-more-bridges-zombienet-tests
svyatonik Jan 22, 2024
99a0457
-q in killall
svyatonik Jan 22, 2024
6e7ff0a
Merge branch 'sv-more-bridges-zombienet-tests' of https://github.com/…
svyatonik Jan 22, 2024
574d4d3
more fixes
svyatonik Jan 22, 2024
cba3d3f
more fixes
svyatonik Jan 22, 2024
cb35c21
one more fix
svyatonik Jan 22, 2024
54dbf94
final (?) fix for test 3
svyatonik Jan 22, 2024
49f644d
also call sync-exit
svyatonik Jan 22, 2024
997b116
increase timeouts again + fix exit condition
svyatonik Jan 22, 2024
276dd76
log events
svyatonik Jan 22, 2024
ae572f5
case for delivery and confirmation tx in single block
svyatonik Jan 22, 2024
dd92da3
start relay after message is sent
svyatonik Jan 22, 2024
520f89a
Merge branch 'master' into sv-more-bridges-zombienet-tests
svyatonik Jan 22, 2024
88077c1
try with nowait
svyatonik Jan 22, 2024
8551934
Merge branch 'master' into sv-more-bridges-zombienet-tests
svyatonik Jan 22, 2024
da8b573
fix function name
svyatonik Jan 22, 2024
d19920e
fix comment
svyatonik Jan 22, 2024
d0885c8
always pass --nonce -1
svyatonik Jan 22, 2024
628bd66
Merge branch 'sv-more-bridges-zombienet-tests' of https://github.com/…
svyatonik Jan 22, 2024
dea6493
txpool tracing for AH collator1
svyatonik Jan 22, 2024
5932dfc
try sleeps after init to avoid reorgs
svyatonik Jan 23, 2024
7f5d1f9
actually I see reorgs with len = 2 blocks, so >= 24 seconds sleep req…
svyatonik Jan 23, 2024
877f77b
I mean seconds
svyatonik Jan 23, 2024
0097812
try bootnode + invulnerable + --discover-local
svyatonik Jan 23, 2024
6681eec
ok, let's try to wait till transactions are finalized
svyatonik Jan 23, 2024
03018da
also in other tests
svyatonik Jan 23, 2024
7e750da
fix wait-account-nonce-finalized.js
svyatonik Jan 23, 2024
643b077
Merge branch 'master' into sv-more-bridges-zombienet-tests
svyatonik Jan 24, 2024
1c7a577
never do process.exit from js scripts
svyatonik Jan 24, 2024
168d323
Merge branch 'sv-more-bridges-zombienet-tests' of https://github.com/…
svyatonik Jan 24, 2024
30d4a9c
rearrange checks
svyatonik Jan 24, 2024
85c90df
--no-mdns
svyatonik Jan 24, 2024
f7030d1
Revert "--no-mdns"
svyatonik Jan 24, 2024
6a4560e
remove bootnode
svyatonik Jan 24, 2024
9c2dacd
flush before bisect
svyatonik Jan 25, 2024
020fd4c
reverting last changes, because they do do not help
svyatonik Jan 25, 2024
5757772
fix???
svyatonik Jan 25, 2024
cc10073
++
svyatonik Jan 25, 2024
29a535a
remove extra traces
svyatonik Jan 25, 2024
435a490
++
svyatonik Jan 25, 2024
8ef9584
one last attempt: disable --force-authoring
svyatonik Jan 26, 2024
3b72a02
green: 1/10
svyatonik Jan 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions .gitlab/pipeline/zombienet/bridges.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@
after_script:
- mkdir -p ./zombienet-logs
# copy logs of tests runner (run-tests.sh)
- cp -r /tmp/bridges-zombienet-tests.*/tmp.*/tmp.* ./zombienet-logs/
- cp -r /tmp/bridges-zombienet-tests.*/test-*.*/* ./zombienet-logs/
# copy logs of all nodes
- cp /tmp/zombie*/logs/* ./zombienet-logs/
- cp /tmp/zombie*/*.log ./zombienet-logs/
# following lines are causing spurious test failures ("At least one of the nodes fails to start")
# retry: 2
# tags:
Expand All @@ -50,5 +51,19 @@ zombienet-bridges-0001-asset-transfer-works:
extends:
- .zombienet-bridges-common
script:
- /home/nonroot/bridges-polkadot-sdk/bridges/zombienet/run-tests.sh --docker
- /home/nonroot/bridges-polkadot-sdk/bridges/zombienet/run-tests.sh --docker --test 1
- echo "Done"

zombienet-bridges-0002-mandatory-headers-synced-while-idle:
extends:
- .zombienet-bridges-common
script:
- /home/nonroot/bridges-polkadot-sdk/bridges/zombienet/run-tests.sh --docker --test 2
- echo "Done"

zombienet-bridges-0003-required-headers-synced-while-active:
extends:
- .zombienet-bridges-common
script:
- /home/nonroot/bridges-polkadot-sdk/bridges/zombienet/run-tests.sh --docker --test 3
- echo "Done"
11 changes: 11 additions & 0 deletions bridges/zombienet/helpers/chains/rococo-at-westend.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = {
grandpaPalletName: "bridgeRococoGrandpa",
parachainsPalletName: "bridgeRococoParachains",
messagesPalletName: "bridgeRococoMessages",
bestBridgedRelayChainGrandpaAuthoritySet: async function(api) {
return await api.query.bridgeRococoGrandpa.currentAuthoritySet();
},
bestBridgedParachainInfo: async function(api) {
return await api.query.bridgeRococoParachains.parasInfo(1013);
},
}
11 changes: 11 additions & 0 deletions bridges/zombienet/helpers/chains/westend-at-rococo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = {
grandpaPalletName: "bridgeWestendGrandpa",
parachainsPalletName: "bridgeWestendParachains",
messagesPalletName: "bridgeWestendMessages",
bestBridgedRelayChainGrandpaAuthoritySet: async function(api) {
return await api.query.bridgeWestendGrandpa.currentAuthoritySet();
},
bestBridgedParachainInfo: async function(api) {
return await api.query.bridgeWestendParachains.parasInfo(1002);
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
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 totalParachainHeaders = 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.ensureOnlyMandatoryGrandpaHeadersImported(
bridgedChain,
apiAtParent,
apiAtCurrent,
currentEvents,
);
totalParachainHeaders += await utils.ensureOnlyInitialParachainHeaderImported(
bridgedChain,
apiAtParent,
apiAtCurrent,
currentEvents,
);
});

// wait given time
await new Promise(resolve => setTimeout(resolve, exitAfterSeconds * 1000));
// if we haven't seen any new GRANDPA or parachain headers => fail
if (totalGrandpaHeaders == 0) {
throw new Error("No bridged relay chain headers imported");
}
if (totalParachainHeaders == 0) {
throw new Error("No bridged parachain headers imported");
}
}

module.exports = { run }
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
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 parachain headers
await utils.ensureOnlyMandatoryGrandpaHeadersImported(
bridgedChain,
apiAtParent,
apiAtCurrent,
currentEvents,
);
await utils.ensureOnlyInitialParachainHeaderImported(
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 parachain header
const newParachainHeaders = utils.countParachainHeaderImports(bridgedChain, currentEvents);
if (newParachainHeaders > 1) {
utils.logEvents(currentEvents);
throw new Error("Unexpected parachain header import: " + newParachainHeaders + " / " + 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 }
41 changes: 41 additions & 0 deletions bridges/zombienet/helpers/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!node
const { WsProvider, ApiPromise } = require("@polkadot/api");
const util = require("@polkadot/util");

async function connect(endpoint, types = {}) {
const provider = new WsProvider(endpoint);
const api = await ApiPromise.create({
provider,
types: {/*
HeaderId: {
number: "u32",
hash: "H256"
}*/
},
throwOnConnect: false,
});
return api;
}

async function test() {
const api = await connect("wss://rococo-bridge-hub-rpc.polkadot.io");

let count = 0;
console.log("begin");
const unsubscribe = await api.rpc.chain.subscribeNewHeads((header) => {
++count;
console.log(count);
return 1;
if (count === 2) {
console.log('2 headers retrieved, unsubscribing');
unsubscribe();
resolve(123)
}
}).then((v) => { console.log(100); });
console.log("end");
//await new Promise(resolve => setTimeout(resolve, 60 * 1000));
console.log(unsubscribe);
console.log("end++");
}

test()
103 changes: 103 additions & 0 deletions bridges/zombienet/helpers/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
module.exports = {
logEvents: function(events) {
let sevents = "";
events.forEach((record) => {
if (sevents != "") {
sevents += ", ";
}
sevents += record.event.section + "::" + record.event.method;
});
console.log("Block events: " + sevents);
},
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,
);
},
countParachainHeaderImports: function(bridgedChain, events) {
return events.reduce(
(count, record) => {
const { event } = record;
if (event.section == bridgedChain.parachainsPalletName && event.method == "UpdatedParachainHead") {
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 bridgedChain.bestBridgedRelayChainGrandpaAuthoritySet(apiAtParent);
const authoritySetIdAtParent = authoritySetAtParent["setId"];

// now read the id of bridged relay chain GRANDPA authorities set at current block
const authoritySetAtCurrent = await bridgedChain.bestBridgedRelayChainGrandpaAuthoritySet(apiAtCurrent);
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;
},
ensureOnlyInitialParachainHeaderImported: async function(
bridgedChain,
apiAtParent,
apiAtCurrent,
currentEvents,
) {
// remember whether we already know bridged parachain header at a parent block
const bestBridgedParachainHeader = await bridgedChain.bestBridgedParachainInfo(apiAtParent);
const hasBestBridgedParachainHeader = bestBridgedParachainHeader.isSome;

// we expect to see: no more than `1` bridged parachain header if there were no parachain header before.
const maxNewParachainHeaders = hasBestBridgedParachainHeader ? 0 : 1;
const newParachainHeaders = module.exports.countParachainHeaderImports(bridgedChain, currentEvents);

// check that our assumptions are correct
if (newParachainHeaders > maxNewParachainHeaders) {
module.exports.logEvents(currentEvents);
throw new Error("Unexpected parachain header import: " + newParachainHeaders + " / " + maxNewParachainHeaders);
}

return newParachainHeaders;
},
}
32 changes: 32 additions & 0 deletions bridges/zombienet/helpers/wait-account-nonce-finalized.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
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 account = args[0];
const expectedNonce = Number(args[1]);

// start listening to new finalized blocks
let nonceMatches = false;
const unsubscribe = await api.rpc.chain.subscribeFinalizedHeads(async function (header) {
const apiAtCurrent = await api.at(header.hash);
const accountState = await apiAtCurrent.query.system.account(account);
const accountNonce = accountState.nonce.toNumber();
console.log("Nonce of account " + account + " is " + accountNonce + " (expected " + expectedNonce + ") at block " + header.hash);
if (accountNonce >= expectedNonce) {
nonceMatches = true;
}
});

// wait until we have received + delivered messages OR until timeout
await utils.pollUntil(
60 * 60,
() => { return nonceMatches; },
() => { unsubscribe(); },
() => { throw new Error("Account transactions are not included into finalized block. Too many reorgs?"); },
);
}

module.exports = { run }
1 change: 0 additions & 1 deletion bridges/zombienet/helpers/wait-hrmp-channel-opened.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ async function run(nodeName, networkInfo, args) {
const api = await zombie.connect(wsUri, userDefinedTypes);

const sibling = args[0];

while (true) {
const messagingStateAsObj = await api.query.parachainSystem.relevantMessagingState();
const messagingState = api.createType("Option<CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot>", messagingStateAsObj);
Expand Down
Loading
Loading