Skip to content
This repository has been archived by the owner on Jun 11, 2024. It is now read-only.

Commit

Permalink
Enhance interop example scripts (#9109)
Browse files Browse the repository at this point in the history
* 🌱 Add hello and react module

* πŸ› Fix schema for react ccCommand

* πŸ”₯ Remove ccmID from CcmSendSuccessEvent topics

* ♻️ Fix schema and ccCommand execute

* Generalize sidechain_registration.ts

* Generalize mainchain_registration.ts

* Revert "πŸ”₯ Remove ccmID from CcmSendSuccessEvent topics"

This reverts commit b4f0e3f.

* transfer_lsk_sidechain_one.ts

* Update mainchain_registration.ts for sc1&2

* Apply suggestions from code review

Co-authored-by: !shan <ishantiw.quasar@gmail.com>

* Fix name bug

* Fix error

* Update sidechain_registration.ts

---------

Co-authored-by: Ishan <ishantiw.quasar@gmail.com>
  • Loading branch information
Tschakki and ishantiw authored Oct 20, 2023
1 parent 7c6f3a6 commit 21f0182
Show file tree
Hide file tree
Showing 36 changed files with 1,130 additions and 45 deletions.
67 changes: 50 additions & 17 deletions examples/interop/common/mainchain_registration.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,42 @@
import { codec, cryptography, apiClient, Transaction } from 'lisk-sdk';
import {
codec,
cryptography,
apiClient,
Transaction,
registrationSignatureMessageSchema,
mainchainRegParams as mainchainRegParamsSchema,
MESSAGE_TAG_CHAIN_REG,
MODULE_NAME_INTEROPERABILITY,
} from 'lisk-framework';
import { COMMAND_NAME_MAINCHAIN_REG } from 'lisk-framework/dist-node/modules/interoperability/constants';

export const registerMainchain = async (
num: string,
sidechainDevValidators: any[],
sidechainValidatorsKeys: any[],
) => {
} from 'lisk-sdk';

/**
* Registers the mainchain to a specific sidechain node.
*
* @example
* ```js
* // Update path to point to the dev-validators.json file of the sidechain which shall be registered on the mainchain
import { keys as sidechainDevValidators } from '../default/dev-validators.json';
* (async () => {
* await registerMainchain("lisk-core","my-lisk-app",sidechainDevValidators);
*})();
* ```
*
* @param mc mainchain alias of the mainchain to be registered.
* @param sc sidechain alias of the sidechain, where the mainchain shall be registered.
* @param sidechainDevValidators the `key` property of the `dev-validators.json` file.
* Includes all keys of the sidechain validators to create the aggregated signature.
*/

export const registerMainchain = async (mc: string, sc: string, sidechainDevValidators: any[]) => {
const { bls, address } = cryptography;

const mainchainClient = await apiClient.createIPCClient(`~/.lisk/mainchain-node-${num}`);
const sidechainClient = await apiClient.createIPCClient(`~/.lisk/pos-sidechain-example-${num}`);
// Connect to the mainchain node
const mainchainClient = await apiClient.createIPCClient(`~/.lisk/${mc}`);
// Connect to the sidechain node
const sidechainClient = await apiClient.createIPCClient(`~/.lisk/${sc}`);

// Get node info data from sidechain and mainchain
const mainchainNodeInfo = await mainchainClient.invoke('system_getNodeInfo');
const sidechainNodeInfo = await sidechainClient.invoke('system_getNodeInfo');

Expand All @@ -28,15 +48,17 @@ export const registerMainchain = async (
height: mainchainNodeInfo.height,
});

// Sort validator list lexicographically after their BLS key
const paramsJSON = {
ownChainID: sidechainNodeInfo.chainID,
ownName: `sidechain_example_${num}`,
ownName: sc.replace(/-/g, '_'),
mainchainValidators: (mainchainActiveValidators as { blsKey: string; bftWeight: string }[])
.map(v => ({ blsKey: v.blsKey, bftWeight: v.bftWeight }))
.sort((a, b) => Buffer.from(a.blsKey, 'hex').compare(Buffer.from(b.blsKey, 'hex'))),
mainchainCertificateThreshold,
};

// Define parameters for the mainchain registration
const params = {
ownChainID: Buffer.from(paramsJSON.ownChainID as string, 'hex'),
ownName: paramsJSON.ownName,
Expand All @@ -47,17 +69,19 @@ export const registerMainchain = async (
mainchainCertificateThreshold: paramsJSON.mainchainCertificateThreshold,
};

// Encode parameters
const message = codec.encode(registrationSignatureMessageSchema, params);

// Get active validators from sidechainChain
// Get active validators from sidechain
const { validators: sidechainActiveValidators } = await sidechainClient.invoke(
'consensus_getBFTParameters',
{ height: sidechainNodeInfo.height },
);

// Add validator private keys to the sidechain validator list
const activeValidatorsWithPrivateKey: { blsPublicKey: Buffer; blsPrivateKey: Buffer }[] = [];
for (const v of sidechainActiveValidators as { blsKey: string; bftWeight: string }[]) {
const validatorInfo = sidechainValidatorsKeys.find(
const validatorInfo = sidechainDevValidators.find(
configValidator => configValidator.plain.blsKey === v.blsKey,
);
if (validatorInfo) {
Expand All @@ -68,11 +92,13 @@ export const registerMainchain = async (
}
}
console.log('Total activeValidatorsWithPrivateKey:', activeValidatorsWithPrivateKey.length);
// Sort active validators from sidechainChain

// Sort active validators from sidechain lexicographically after their BLS public key
activeValidatorsWithPrivateKey.sort((a, b) => a.blsPublicKey.compare(b.blsPublicKey));

const sidechainValidatorsSignatures: { publicKey: Buffer; signature: Buffer }[] = [];
// Sign with each active validator

// Sign parameters with each active sidechain validator
for (const validator of activeValidatorsWithPrivateKey) {
const signature = bls.signData(
MESSAGE_TAG_CHAIN_REG,
Expand All @@ -92,30 +118,37 @@ export const registerMainchain = async (
sidechainValidatorsSignatures,
);

// Get public key and nonce of the sender account
const relayerKeyInfo = sidechainDevValidators[0];
const { nonce } = await sidechainClient.invoke<{ nonce: string }>('auth_getAuthAccount', {
address: address.getLisk32AddressFromPublicKey(Buffer.from(relayerKeyInfo.publicKey, 'hex')),
});

// Add aggregated signature to the parameters of the mainchain registration
const mainchainRegParams = {
...paramsJSON,
signature: signature.toString('hex'),
aggregationBits: aggregationBits.toString('hex'),
};

// Create registerMainchain transaction
const tx = new Transaction({
module: MODULE_NAME_INTEROPERABILITY,
command: COMMAND_NAME_MAINCHAIN_REG,
command: 'registerMainchain',
fee: BigInt(2000000000),
params: codec.encodeJSON(mainchainRegParamsSchema, mainchainRegParams),
nonce: BigInt(nonce),
senderPublicKey: Buffer.from(relayerKeyInfo.publicKey, 'hex'),
signatures: [],
});

// Sign the transaction
tx.sign(
Buffer.from(sidechainNodeInfo.chainID as string, 'hex'),
Buffer.from(relayerKeyInfo.privateKey, 'hex'),
);

// Post the transaction to a sidechain node
const result = await sidechainClient.invoke<{
transactionId: string;
}>('txpool_postTransaction', {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,69 +1,83 @@
import { apiClient, codec, cryptography, Transaction } from 'lisk-sdk';
import { apiClient, codec, sidechainRegParams, cryptography, Transaction } from 'lisk-sdk';
// Replace this with the path to a file storing the public and private key of a mainchain account who will send the sidechain registration transaction.
// (Can be any account with enough tokens).
import { keys } from '../default/dev-validators.json';
import { sidechainRegParams } from 'lisk-framework';

(async () => {
const { address } = cryptography;

const SIDECHAIN_ARRAY = ['one', 'two'];
// Replace this with alias of the sidechain node(s)
const SIDECHAIN_ARRAY = ['pos-sidechain-example-one', 'pos-sidechain-example-two'];
// Replace this with the alias of the mainchain node(s), e.g. lisk-core
// Note: Number of mainchain nodes should be equal to sidechain nodes, for this script to work properly.
const MAINCHAIN_ARRAY = ['mainchain-node-one', 'mainchain-node-two'];
let i = 0;
for (const nodeAlias of SIDECHAIN_ARRAY) {
const sidechainClient = await apiClient.createIPCClient(
`~/.lisk/pos-sidechain-example-${nodeAlias}`,
);
const mainchainClient = await apiClient.createIPCClient(`~/.lisk/mainchain-node-${nodeAlias}`);
// Connect to the sidechain node
const sidechainClient = await apiClient.createIPCClient(`~/.lisk/${nodeAlias}`);
// Connect to the mainchain node
const mainchainClient = await apiClient.createIPCClient(`~/.lisk/${MAINCHAIN_ARRAY[i]}`);

// Get node info data from sidechain and mainchain
const sidechainNodeInfo = await sidechainClient.invoke('system_getNodeInfo');
const mainchainNodeInfo = await mainchainClient.invoke('system_getNodeInfo');
// Get active validators from sidechainchain
const { validators: sidehcainActiveValidators, certificateThreshold } =

// Get info about the active sidechain validators and the certificate threshold
const { validators: sidechainActiveValidators, certificateThreshold } =
await sidechainClient.invoke('consensus_getBFTParameters', {
height: sidechainNodeInfo.height,
});

(sidehcainActiveValidators as { blsKey: string; bftWeight: string }[]).sort((a, b) =>
// Sort validator list lexicographically after their BLS key
(sidechainActiveValidators as { blsKey: string; bftWeight: string }[]).sort((a, b) =>
Buffer.from(a.blsKey, 'hex').compare(Buffer.from(b.blsKey, 'hex')),
);

// Define parameters for the sidechain registration
const params = {
sidechainCertificateThreshold: certificateThreshold,
sidechainValidators: sidehcainActiveValidators,
sidechainValidators: sidechainActiveValidators,
chainID: sidechainNodeInfo.chainID,
name: `sidechain_example_${nodeAlias}`,
name: nodeAlias.replace(/-/g, '_'),
};

const relayerkeyInfo = keys[2];
// Get public key and nonce of the sender account
const relayerKeyInfo = keys[2];
const { nonce } = await mainchainClient.invoke<{ nonce: string }>('auth_getAuthAccount', {
address: address.getLisk32AddressFromPublicKey(Buffer.from(relayerkeyInfo.publicKey, 'hex')),
address: address.getLisk32AddressFromPublicKey(Buffer.from(relayerKeyInfo.publicKey, 'hex')),
});

// Create registerSidechain transaction
const tx = new Transaction({
module: 'interoperability',
command: 'registerSidechain',
fee: BigInt(2000000000),
params: codec.encodeJSON(sidechainRegParams, params),
nonce: BigInt(nonce),
senderPublicKey: Buffer.from(relayerkeyInfo.publicKey, 'hex'),
senderPublicKey: Buffer.from(relayerKeyInfo.publicKey, 'hex'),
signatures: [],
});

// Sign the transaction
tx.sign(
Buffer.from(mainchainNodeInfo.chainID as string, 'hex'),
Buffer.from(relayerkeyInfo.privateKey, 'hex'),
Buffer.from(relayerKeyInfo.privateKey, 'hex'),
);

// Post the transaction to a mainchain node
const result = await mainchainClient.invoke<{
transactionId: string;
}>('txpool_postTransaction', {
transaction: tx.getBytes().toString('hex'),
});

console.log(
`Sent sidechain registration transaction on mainchain node ${nodeAlias}. Result from transaction pool is: `,
`Sent sidechain registration transaction on mainchain node ${MAINCHAIN_ARRAY[1]}. Result from transaction pool is: `,
result,
);
i += 1;

// Wait in case there are more elements in the SIDECHAIN_ARRAY, after performing another loop with the next element.
const wait = async (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
if (i < SIDECHAIN_ARRAY.length) {
const WAIT_PERIOD = 10000;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { apiClient, codec, cryptography, Schema, Transaction } from 'lisk-sdk';
// Replace this with the path to a file storing the public and private key of a mainchain account who will send the sidechain registration transaction.
// (Can be any account with enough tokens).
import { keys } from '../default/dev-validators.json';
type ModulesMetadata = [
{
Expand All @@ -12,25 +14,30 @@ type ModulesMetadata = [
const { address } = cryptography;

const nodeAlias = 'one';
// Update this with the Token ID of the token you wish to transfer
const tokenID = Buffer.from('0400000000000000', 'hex');
const sidechainID = Buffer.from('04000001', 'hex'); // Update this to send to another sidechain
// Update this with the chain ID of the receiving chain
const sidechainID = Buffer.from('04000001', 'hex');
// Update this with the recipient address
const recipientLSKAddress = 'lskxz85sur2yo22dmcxybe39uvh2fg7s2ezxq4ny9';
const recipientAddress = address.getAddressFromLisk32Address(recipientLSKAddress);

// Connect to the mainchain node
const mainchainClient = await apiClient.createIPCClient(`~/.lisk/mainchain-node-one`);

// Get node info data from mainchain
const mainchainNodeInfo = await mainchainClient.invoke('system_getNodeInfo');

// Get schema for the transferCrossChain command
const { modules: modulesMetadata } = await mainchainClient.invoke<{
modules: ModulesMetadata;
}>('system_getMetadata');

const tokenMetadata = modulesMetadata.find(m => m.name === 'token');

const ccTransferCMDSchema = tokenMetadata?.commands.filter(
cmd => cmd.name == 'transferCrossChain',
)[0].params as Schema;

// Define parameters for the cc transfer
const params = {
tokenID,
amount: BigInt('10000000000'),
Expand All @@ -41,11 +48,13 @@ type ModulesMetadata = [
messageFeeTokenID: tokenID,
};

// Get public key and nonce of the sender account
const relayerkeyInfo = keys[2];
const { nonce } = await mainchainClient.invoke<{ nonce: string }>('auth_getAuthAccount', {
address: address.getLisk32AddressFromPublicKey(Buffer.from(relayerkeyInfo.publicKey, 'hex')),
});

// Create transferCrossChain transaction
const tx = new Transaction({
module: 'token',
command: 'transferCrossChain',
Expand All @@ -56,11 +65,13 @@ type ModulesMetadata = [
signatures: [],
});

// Sign the transaction
tx.sign(
Buffer.from(mainchainNodeInfo.chainID as string, 'hex'),
Buffer.from(relayerkeyInfo.privateKey, 'hex'),
);

// Post the transaction to a mainchain node
const result = await mainchainClient.invoke<{
transactionId: string;
}>('txpool_postTransaction', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@
"ccuFee": "500000000",
"receivingChainIPCPath": "~/.lisk/mainchain-node-one",
"registrationHeight": 10
},
"dashboard": {
"applicationUrl": "ws://127.0.0.1:7885/rpc-ws",
"port": 4006
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { keys as sidechainValidatorsKeys } from '../../config/default/dev-validators.json';
import { keys as sidechainDevValidators } from '../default/dev-validators.json';
import { registerMainchain } from '../../../common/mainchain_registration';

(async () => {
await registerMainchain('one', sidechainDevValidators, sidechainValidatorsKeys);
await registerMainchain(
'mainchain-node-one',
'pos-sidechain-example-one',
sidechainDevValidators,
);
})();
6 changes: 6 additions & 0 deletions examples/interop/pos-sidechain-example-one/src/app/app.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { Application, PartialApplicationConfig } from 'lisk-sdk';
import { registerModules } from './modules';
import { registerPlugins } from './plugins';
import { HelloModule } from './modules/hello/module';

export const getApplication = (config: PartialApplicationConfig): Application => {
const { app } = Application.defaultApplication(config);

const helloModule = new HelloModule();
app.registerModule(helloModule);

app.registerInteroperableModule(helloModule);

registerModules(app);
registerPlugins(app);

Expand Down
Loading

0 comments on commit 21f0182

Please sign in to comment.