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

Restart pod when event proof is not found #108

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
"allowImportExportEverywhere": true
},
"rules": {
"no-constant-condition": "off"
}
}
2 changes: 1 addition & 1 deletion .github/workflows/pr-any.yml
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ jobs:
run: |
docker network create my-bridge-net
docker run --pull=always --network=my-bridge-net --name ethNode -p 8545:8545 -p 30303:30303 -d ethereumoptimism/hardhat
docker run --pull=always --network=my-bridge-net --name testnet_node_alice -p 9944:9944 -d cennznet/cennznet:2.1.0 --dev --tmp --unsafe-ws-external --unsafe-rpc-external --eth-http=http://ethNode:8545 --no-mdns
docker run --pull=always --network=my-bridge-net --name testnet_node_alice -p 9944:9944 -d cennznet/cennznet:latest --dev --tmp --unsafe-ws-external --unsafe-rpc-external --eth-http=http://ethNode:8545 --no-mdns
docker exec testnet_node_alice curl ethNode:8545
# - name: deposit/withdraw scenarios for erc20 tokens
# env:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const ROPSTEN_V1 = {
const ROPSTEN_V2 = {
bridge: "0x452b8dd7b00D51e48cEF6254a48B7426d44658B8",
peg: "0x4C411B3Bf36D6DE908C6f4256a72B85E3f2B00bF",
wcennz: "0xcdA767469D434c14D320e1A78D5b612B72B52bf0",
wcennz: "0x673F0244bd0AEbd9918ef089f75C503Df9bfBb38",
}
```

Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"description": "CENNZnet <> Eth Bridge",
"main": "index.js",
"scripts": {
"api": "nodemon src/api/index.js",
"api": "node src/api/index.js",
"api:dev": "nodemon src/api/index.js",
"build": "rm -rf artifacts & hardhat compile",
"build:abi": "yarn build && (mkdir abi || true) && cp artifacts/contracts/**/*.json abi/ && rm abi/*.dbg.json",
"deploy": "hardhat run --network localhost scripts/deploy.js",
Expand Down Expand Up @@ -62,7 +63,7 @@
"winston": "^3.2.1"
},
"devDependencies": {
"@cennznet/api": "2.1.0-rc.4",
"@cennznet/api": "2.1.1-alpha.2",
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-etherscan": "^2.1.7",
"@nomiclabs/hardhat-waffle": "^2.0.1",
Expand Down
7 changes: 4 additions & 3 deletions scripts/e2e.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { Api } = require('@cennznet/api');
const { Keyring } = require('@polkadot/keyring');
const { encodeAddress, Keyring } = require('@polkadot/keyring');
const { ethers } = require("hardhat");

async function main() {
Expand Down Expand Up @@ -96,8 +96,9 @@ async function main() {
if (status.isInBlock) {
for (const {event: {method, section, data}} of events) {
console.log('\t', `: ${section}.${method}`, data.toString());
const [, claimer] = data;
if (section === 'erc20Peg' && method == 'Erc20Claim' && claimer && claimer.toString() === alice.address) {
const [, address] = data;
if (section === 'erc20Peg' && method == 'Erc20Claim' && address &&
(address.toString() === alice.address || address.toString() === encodeAddress(claim.beneficiary))) {
eventClaimId = data[0];
console.log('*******************************************');
console.log('Deposit claim on CENNZnet side started for claim Id', eventClaimId.toString());
Expand Down
7 changes: 4 additions & 3 deletions scripts/ethEndToEndTest.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

const { Api } = require('@cennznet/api');
const { Keyring } = require('@polkadot/keyring');
const { encodeAddress, Keyring } = require('@polkadot/keyring');
const { ethers } = require("hardhat");

async function main() {
Expand Down Expand Up @@ -99,8 +99,9 @@ async function main() {
if (status.isInBlock) {
for (const {event: {method, section, data}} of events) {
console.log('\t', `: ${section}.${method}`, data.toString());
const [, claimer] = data;
if (section === 'erc20Peg' && method == 'Erc20Claim' && claimer && claimer.toString() === alice.address) {
const [, address] = data;
if (section === 'erc20Peg' && method == 'Erc20Claim' && address &&
(address.toString() === alice.address || address.toString() === encodeAddress(claim.beneficiary))) {
eventClaimId = data[0];
console.log('*******************************************');
console.log('Deposit claim on CENNZnet side started for claim Id', eventClaimId.toString());
Expand Down
6 changes: 4 additions & 2 deletions scripts/ethereumEventPoller.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ async function pollDepositEvents( networkName, interval, pegContractAddress, pro
api = await Api.create({network: networkName});
let rabbit = await amqp.connect(process.env.RABBIT_URL);
let channel = await rabbit.createChannel();
await channel.assertQueue(TOPIC_CENNZnet_CONFIRM);
const messageTimeout = 60000 * 5; //5 minutes
await channel.assertQueue(TOPIC_CENNZnet_CONFIRM,{durable: true, messageTtl: messageTimeout});
if (networkName === 'azalea') {
provider = new ethers.providers.AlchemyProvider(process.env.ETH_NETWORK,
process.env.AlCHEMY_API_KEY
Expand Down Expand Up @@ -74,7 +75,8 @@ async function pollWithdrawEvents( networkName, interval, pegContractAddress, br
if(mongoose.connection.readyState !== 1) await mongoose.connect(connectionStr);
let rabbit = await amqp.connect(process.env.RABBIT_URL);
let channel = await rabbit.createChannel();
await channel.assertQueue(TOPIC_CENNZnet_CONFIRM);
const messageTimeout = 60000 * 5; //5 minutes
await channel.assertQueue(TOPIC_CENNZnet_CONFIRM,{durable: true, messageTtl: messageTimeout});
if (networkName === 'azalea') {
provider = new ethers.providers.AlchemyProvider(process.env.ETH_NETWORK,
process.env.AlCHEMY_API_KEY
Expand Down
14 changes: 7 additions & 7 deletions scripts/subscribeEthereumDeposit.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { Api } = require('@cennznet/api');
const { Keyring } = require('@polkadot/keyring');
const { encodeAddress, Keyring } = require('@polkadot/keyring');
const logger = require('./logger');
const mongoose = require('mongoose');
const { BridgeClaim, ClaimEvents } = require('../src/mongo/models');
Expand All @@ -9,7 +9,6 @@ const { hexToU8a } = require("@polkadot/util");
const pegAbi = require("../abi/ERC20Peg.json").abi;
const BigNumber = require("bignumber.js");
const amqp = require("amqplib");

require("dotenv").config();

async function airDrop(claimId, signer, api, spendingAssetId) {
Expand Down Expand Up @@ -76,8 +75,9 @@ async function sendClaim(claim, transactionHash, api, nonce, signer) {
const block = await api.rpc.chain.getBlock(blockHash);
const blockNumber = block.block.header.number.toNumber();
for (const {event: {method, section, data}} of events) {
const [, claimer] = data;
if (section === 'erc20Peg' && method == 'Erc20Claim' && claimer && claimer.toString() === signer.address) {
const [, address] = data;
if (section === 'erc20Peg' && method == 'Erc20Claim' && address &&
(address.toString() === signer.address || address.toString() === encodeAddress(claim.beneficiary))) {
const eventClaimId = data[0].toString();
logger.info('CLAIM: *******************************************');
logger.info('CLAIM: at block number: ',blockNumber);
Expand Down Expand Up @@ -212,14 +212,14 @@ async function mainPublisher(networkName, pegContractAddress, providerOverride=
logger.info(`HEALTH CHECK => OK`);
logger.info(`At blocknumber: ${blockNumber}`);
});

const messageTimeout = 60000 * 5; //5 minutes
const peg = new ethers.Contract(pegContractAddress, pegAbi, provider);
logger.info(`Connecting to CENNZnet peg contract ${pegContractAddress}...`);
const eventConfirmations = (await api.query.ethBridge.eventConfirmations()).toNumber();
let channel;
if (channelOverride) channel = channelOverride;
else channel = await rabbit.createChannel();
await channel.assertQueue(TOPIC_CENNZnet_CONFIRM);
await channel.assertQueue(TOPIC_CENNZnet_CONFIRM, {durable: true, messageTtl: messageTimeout});
// On eth side deposit push pub sub queue with the data, if bridge is paused, update tx status as bridge paused
peg.on("Deposit", async (sender, tokenAddress, amount, cennznetAddress, eventInfo) => {
logger.info(`Got the event...${JSON.stringify(eventInfo)}`);
Expand Down Expand Up @@ -397,4 +397,4 @@ let firstMessage = true;
if(state === "publisher") mainPublisher(networkName, pegContractAddress).catch((err) => logger.error(err));
else if (state === "subscriber") mainSubscriber(networkName).catch((err) => logger.error(err));

module.exports = {wait, handleDepositEvent, mainPublisher, mainSubscriber, TOPIC_VERIFY_CONFIRM, TOPIC_CENNZnet_CONFIRM}
module.exports = {wait, handleDepositEvent, mainPublisher, mainSubscriber, TOPIC_VERIFY_CONFIRM, TOPIC_CENNZnet_CONFIRM}
109 changes: 56 additions & 53 deletions scripts/subscribeEventProof.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const { EventProcessed } = require('../src/mongo/models');
const ethers = require('ethers');
const bridgeAbi = require("../abi/CENNZnetBridge.json").abi;

const timeoutMs = 20000;
const timeoutMs = 40000;
const BUFFER = 1000;
// Ignore if validator public key is 0x000..
const IGNORE_KEY = '0x000000000000000000000000000000000000000000000000000000000000000000';
Expand Down Expand Up @@ -60,61 +60,62 @@ async function sendSlackNotification(message) {
// Submit the event proof on Ethereum Bridge contract
async function getEventPoofAndSubmit(api, eventId, bridge, txExecutor, newValidatorSetId, blockHash, provider) {
const eventExistsOnEth = await bridge.eventIds(eventId.toString());
if (eventExistsOnEth) return; // return if event proof already exist on Ethereum
const eventProof = await withTimeout(api.derive.ethBridge.eventProof(eventId), timeoutMs);
if (eventProof && !eventExistsOnEth) {
const newValidators = await extractNewValidators(api, blockHash);
logger.info(`IMP Sending setValidators tx with the account: ${txExecutor.address}`);
logger.info(`IMP Parameters :::`);
logger.info(`IMP newValidators:${newValidators}`);
logger.info(`IMP newValidatorSetId: ${newValidatorSetId}`);
logger.info(`IMP event proof::${JSON.stringify(eventProof)}`);
const currentValidators = await extractCurrentValidators(api, blockHash);
logger.info(`IMP currentValidators:${currentValidators}`);
const proof = {
eventId: eventProof.eventId,
validatorSetId: eventProof.validatorSetId,
r: eventProof.r,
s: eventProof.s,
v: eventProof.v,
validators: currentValidators
};
try {
const gasPrice = await provider.getGasPrice();
logger.info('gas price::', gasPrice.toString());
// Take 5 percent of current gas price
const percentGasPrice = gasPrice.mul(5).div(100);
logger.info('percentGasPrice:',percentGasPrice.toString());
const increasedGasPrice = gasPrice.add(percentGasPrice);
logger.info('Gas price nw;:', gasPrice.toString());

const gasEstimated = await bridge.estimateGas.setValidators(newValidators, newValidatorSetId, proof, {gasLimit: 5000000, gasPrice: increasedGasPrice});

logger.info(JSON.stringify(await bridge.setValidators(newValidators, newValidatorSetId, proof, {gasLimit: gasEstimated.add(BUFFER), gasPrice: increasedGasPrice})));
await updateLastEventProcessed(eventId, blockHash.toString());
const balance = await provider.getBalance(txExecutor.address);
logger.info(`IMP Balance is: ${balance}`);

logger.info(`IMP Gas price: ${gasPrice.toString()}`);
const gasRequired = gasEstimated.mul(gasPrice);
logger.info(`IMP Gas required: ${gasRequired.toString()}`);
if (balance.lt(gasRequired.mul(2))) {
const message = ` 🚨 To keep the validator relayer running, topup the eth account ${txExecutor.address} on CENNZnets ${process.env.NETWORK} chain`;
await sendSlackNotification(message);
}
} catch (e) {
logger.warn('Something went wrong:');
logger.error(`IMP Error: ${e.stack}`);
// send slack notification when proof submission fails
const message = ` 🚨 Issue while submitting validator set on ethereum bridge
proof: ${JSON.stringify(proof)}
newValidators: ${newValidators}
newValidatorSetId: ${newValidatorSetId}
on CENNZnets ${process.env.NETWORK} chain`;
await sendSlackNotification(message);
}
} else if (!eventProof){
if (!eventProof) {
logger.info(`IMP Could not retrieve event proof for event id ${eventId} from derived
query api.derive.ethBridge.eventProof at ${timeoutMs} timeout`);
process.exit(1);
KarishmaBothara marked this conversation as resolved.
Show resolved Hide resolved
}
const newValidators = await extractNewValidators(api, blockHash);
logger.info(`IMP Sending setValidators tx with the account: ${txExecutor.address}`);
logger.info(`IMP Parameters :::`);
logger.info(`IMP newValidators:${newValidators}`);
logger.info(`IMP newValidatorSetId: ${newValidatorSetId}`);
logger.info(`IMP event proof::${JSON.stringify(eventProof)}`);
const currentValidators = await extractCurrentValidators(api, blockHash);
logger.info(`IMP currentValidators:${currentValidators}`);
const proof = {
eventId: eventProof.eventId,
validatorSetId: eventProof.validatorSetId,
r: eventProof.r,
s: eventProof.s,
v: eventProof.v,
validators: currentValidators
};
try {
const gasPrice = await provider.getGasPrice();
logger.info('gas price::', gasPrice.toString());
// Take 5 percent of current gas price
const percentGasPrice = gasPrice.mul(5).div(100);
logger.info('percentGasPrice:',percentGasPrice.toString());
const increasedGasPrice = gasPrice.add(percentGasPrice);
logger.info('Gas price nw;:', gasPrice.toString());

const gasEstimated = await bridge.estimateGas.setValidators(newValidators, newValidatorSetId, proof, {gasLimit: 5000000, gasPrice: increasedGasPrice});

logger.info(JSON.stringify(await bridge.setValidators(newValidators, newValidatorSetId, proof, {gasLimit: gasEstimated.add(BUFFER), gasPrice: increasedGasPrice})));
await updateLastEventProcessed(eventId, blockHash.toString());
const balance = await provider.getBalance(txExecutor.address);
logger.info(`IMP Balance is: ${balance}`);

logger.info(`IMP Gas price: ${gasPrice.toString()}`);
const gasRequired = gasEstimated.mul(gasPrice);
logger.info(`IMP Gas required: ${gasRequired.toString()}`);
if (balance.lt(gasRequired.mul(2))) {
const message = ` 🚨 To keep the validator relayer running, topup the eth account ${txExecutor.address} on CENNZnets ${process.env.NETWORK} chain`;
await sendSlackNotification(message);
}
} catch (e) {
logger.warn('Something went wrong:');
logger.error(`IMP Error: ${e.stack}`);
// send slack notification when proof submission fails
const message = ` 🚨 Issue while submitting validator set on ethereum bridge
proof: ${JSON.stringify(proof)}
newValidators: ${newValidators}
newValidatorSetId: ${newValidatorSetId}
on CENNZnets ${process.env.NETWORK} chain`;
await sendSlackNotification(message);
}
}

Expand Down Expand Up @@ -203,6 +204,8 @@ async function withTimeout(promise, timeoutMs) {
return Promise.race ([
promise,
new Promise ((resolve) => {
const randomValue = Math.random();
if (networkName.toLowerCase() === 'nikau' && randomValue > 0.5) resolve(null);
setTimeout(() => {
resolve(null);
}, timeoutMs);
Expand Down
42 changes: 34 additions & 8 deletions scripts/subscribeWithdrawTx.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const { u8aToString } = require('@polkadot/util');
const ethers = require('ethers');
const bridgeAbi = require("../abi/CENNZnetBridge.json").abi;
const pegAbi = require("../abi/ERC20Peg.json").abi;
const {cvmToAddress} = require("@cennznet/types/utils");

let registeredAsset;
const timeoutMs = 60000;
Expand Down Expand Up @@ -69,6 +70,37 @@ function scientificToDecimal(number) {
return `${sign}${number}`;
}

function isWithdrawErc20Peg(tx) {
const extrinsicCall = tx.method.toHuman();
return (extrinsicCall.args.call.method === 'withdraw' && extrinsicCall.args.call.section === 'erc20Peg');
}

function fetchExtrinsicDetail(block) {
const withdrawTokenExt
= block.block.extrinsics.find(ex => ex.isSigned === true && ex.method.method === 'withdraw' && ex.method.section === 'erc20Peg');
if (withdrawTokenExt) {
return [withdrawTokenExt.hash.toString(), withdrawTokenExt.signer]; // extrinsicHash, signer
}
// check if blocks has any ethWallet txs
const ethWalletExts
= block.block.extrinsics.filter(ex => ex.method.method === 'call' && ex.method.section === 'ethWallet');

if (ethWalletExts.length > 0) {
// find the one with erc20Peg withdraw extrinsic
const pegWithdrawTx = ethWalletExts.find(tx => isWithdrawErc20Peg(tx));
if (!pegWithdrawTx) return [];
const extrinsicCall = pegWithdrawTx.method.toHuman();
const evmAddress = extrinsicCall.args.eth_address;
if (evmAddress) {
console.log('cennznet address:',cvmToAddress(evmAddress));
return [pegWithdrawTx.hash.toString(), cvmToAddress(evmAddress)]; // convert to cennznet address and return
} else {
return [];
}
}
return [];
}

async function getWithdrawProofAndUpdateDB(api, eventDetails, blockHash, bridge) {
try {
const [eventId, assetId, amountRaw, beneficiary] = eventDetails;
Expand All @@ -80,14 +112,8 @@ async function getWithdrawProofAndUpdateDB(api, eventDetails, blockHash, bridge)
logger.info(`IMP WITHDRAW newValidators:${newValidators}`);
logger.info(`IMP WITHDRAW event proof::${JSON.stringify(eventProof)}`);
const rawBlock = await api.rpc.chain.getBlock(blockHash);
const block = rawBlock.toHuman();
const withdrawExtIndex
= block.block.extrinsics.findIndex(ex => ex.isSigned === true && ex.method.method === 'withdraw' && ex.method.section === 'erc20Peg');
const withdrawExt = block.block.extrinsics[withdrawExtIndex];
const rawExt = rawBlock.block.extrinsics[withdrawExtIndex];
const txHash = api.registry.createType('Extrinsic', rawExt).hash.toHex();
const cennznetAddress = withdrawExt ? withdrawExt.signer : '';
console.log('withdrawExt::',withdrawExt);

const [txHash, cennznetAddress] = fetchExtrinsicDetail(rawBlock);

const proof = {
eventId: eventProof.eventId,
Expand Down
15 changes: 14 additions & 1 deletion src/mongo/models.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,17 @@ const proofsSchema = new Schema( {
validators: []
}, { collection: EVENT_PROOF });

const LastBlockScannedCol = "LastBlockScan";
const LastBlockScanSchema = new Schema(
{
_id: String,
processedBlock: { type: String, default: "0" },
finalizedBlock: String,
},
{ collection: LastBlockScannedCol }
);


module.exports = {
BridgeClaim: mongoose.model('BridgeClaim', BridgeClaimSchema),
BRIDGE_CLAIM,
Expand All @@ -70,5 +81,7 @@ module.exports = {
WithdrawProof: mongoose.model('WithdrawProof', WithdrawProofSchema),
WITHDRAW_PROOF,
EventProof: mongoose.model('EventProof', proofsSchema),
EVENT_PROOF
EVENT_PROOF,
LastBlockScan: mongoose.model("LastBlockScan", LastBlockScanSchema),
LastBlockScannedCol,
};
Loading