@kadena/hardhat-chainweb
Version:
Hardhat plugin for Kadena's Chainweb network
154 lines • 6.69 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ChainwebNetwork = void 0;
const ethers_1 = require("ethers");
const chainweb_graph_js_1 = require("./chainweb-graph.js");
const sleep_js_1 = require("./sleep.js");
const ethers_helpers_js_1 = require("./ethers-helpers.js");
const logger_js_1 = require("./logger.js");
const chain_js_1 = require("./chain.js");
const hardhat_ethers_provider_js_1 = require("@nomicfoundation/hardhat-ethers/internal/hardhat-ethers-provider.js");
const pure_utils_js_1 = require("../pure-utils.js");
class ChainwebNetwork {
constructor(config) {
this.config = config;
this.logger = {
info: (msg) => (0, logger_js_1.logInfo)('reset', '-', msg),
error: (msg) => (0, logger_js_1.logError)('reset', '-', msg),
};
this.chains = makeChainweb(this.logger, this.config);
this.graph = config.chainweb.graph;
}
getProvider(cid) {
const chain = this.chains[cid];
if (chain === undefined) {
throw new Error(`Chain not found in Chainweb ${cid}`);
}
const provider = chain.provider;
if (provider === null) {
throw new Error(`Chain network is not running ${cid}`);
}
return provider;
}
async start() {
try {
this.logger.info('Starting chain networks');
await Promise.all(Object.values(this.chains).map((chain) => {
return chain.start();
}));
this.logger.info('Chainweb chains initialized');
}
catch (e) {
this.logger.error(`Failure while starting networks: ${e}, ${e.stack}`);
await this.stop();
}
}
async stop() {
this.logger.info('Stopping chain networks');
await Promise.all(Object.values(this.chains).map((chain) => chain.stop()));
this.logger.info('Stopped chain networks');
}
// Mock getProof:
//
// Call our chainweb SPV api with the necesasry proof parameters.
//
// This mocks the call of the follwing API:
//
// http://localhost:1848/chainweb/0.0/evm-development/chain/${trgChain}/spv/chain/${origin.chain}/height/${origin.height}/transaction/${origin.txIdx}/event/${origin.eventIdx}
//
async getSpvProof(trgChain, origin) {
// get origin chain
const provider = new hardhat_ethers_provider_js_1.HardhatEthersProvider(this.getProvider(Number(origin.chain)), `${(0, pure_utils_js_1.getNetworkStem)(this.config.chainwebName)}${origin.chain}`);
// Query Event information from origin chain
const blockLogs = await provider.getLogs({
fromBlock: origin.height,
toBlock: origin.height,
});
const txLogs = blockLogs.filter((l) => BigInt(l.transactionIndex) === origin.txIdx);
const log = txLogs[Number(origin.eventIdx)];
if (log === undefined || log.removed) {
new Error('No log entry found at origin');
}
const topics = log.topics;
if (topics.length != 4) {
throw new Error(`Expected exactly four topics at origin, but got ${topics.length}`);
}
// for target chain to advance enough blocks such that the origin information
// is available.
//
// TODO should fail at least once so that the caller has to wait?
//
const src = this.chains[Number(origin.chain)];
const trg = this.chains[trgChain];
if (src === undefined || trg === undefined) {
throw new Error(`Chain not found in Chainweb`);
}
const dist = BigInt((0, chainweb_graph_js_1.distance)(src.cid, trg.cid, this.graph));
let trgHeight = BigInt(await trg.getBlockNumber());
while (trgHeight < origin.height + dist) {
console.log(`waiting for SPV proof to become available on chain ${trgChain}; current height ${trgHeight}; required height ${origin.height + dist}`);
await trg.mineRequest();
(0, sleep_js_1.sleep)(100);
trgHeight = BigInt(await trg.getBlockNumber());
}
const coder = ethers_1.ethers.AbiCoder.defaultAbiCoder();
// FIXME: double check the event signature
// (uint32,address,uint64,uint64,uint64)
const xorigin = Object.values({
chainId: origin.chain,
address: log.address,
height: origin.height,
txIdx: origin.txIdx,
eventIdx: origin.eventIdx,
});
// (uint32,address,uint64,(uint32,address,uint64,uint64,uint64))
const xmsg = Object.values({
trgChainId: ethers_1.ethers.toNumber(topics[1]),
trgAddress: (0, ethers_helpers_js_1.wordToAddress)(topics[2]),
opType: ethers_1.ethers.toNumber(topics[3]),
data: coder.decode(['bytes'], log.data)[0],
origin: xorigin,
});
const params = 'tuple(uint32,address,uint64,bytes,tuple(uint32,address,uint64,uint64,uint64))';
const payload = coder.encode([params], [xmsg]);
const hash = ethers_1.ethers.keccak256(payload);
return ethers_1.ethers.concat([hash, payload]);
}
}
exports.ChainwebNetwork = ChainwebNetwork;
/* *************************************************************************** */
/* Chainweb Network */
function makeChainweb(logger, config) {
var _a;
const graph = config.chainweb.graph;
const networks = config.networks;
// Create Individual Chains
logger.info('creating chains');
const chains = {};
for (const networkName in networks) {
if (networkName.includes((0, pure_utils_js_1.getNetworkStem)(config.chainwebName))) {
const networkConfig = networks[networkName];
if ((_a = config.overrideForking) === null || _a === void 0 ? void 0 : _a.url) {
networkConfig.forking = { enabled: true, ...config.overrideForking };
}
chains[networkConfig.chainwebChainId] = new chain_js_1.Chain(networkConfig, config.chainweb.logging);
}
}
// Put Chains into the Chainweb Graph
logger.info('integrating chains into Chainweb');
for (const c in chains) {
if (graph[c] === undefined) {
console.log(c, graph);
throw new Error(`Missing configuration for chain ${c}`);
}
chains[c].adjacents = graph[c].map((x) => {
const a = chains[x];
if (a === undefined) {
throw new Error(`Missing configuration for chain ${x}`);
}
return chains[x];
});
}
return chains;
}
//# sourceMappingURL=chainweb.js.map