mirror of
https://github.com/cerc-io/watcher-ts
synced 2026-09-11 18:24:05 +00:00
Send mobymask p2p messages to laconicd (#339)
* Use laconic ETH RPC endpoint for querying * Run peer with message handler to send tx to laconic * Handle revoke messages in mobymask p2p * Set tx gasLimit explicitly for slow eth_estimateGas call * Convert delegationHash to hex string before broadcasting json
This commit is contained in:
@@ -11,7 +11,7 @@ import { ethers } from 'ethers';
|
||||
import { JsonFragment } from '@ethersproject/abi';
|
||||
import { JsonRpcProvider } from '@ethersproject/providers';
|
||||
import { EthClient } from '@cerc-io/ipld-eth-client';
|
||||
import { MappingKey, StorageLayout } from '@cerc-io/solidity-mapper';
|
||||
import { MappingKey, StorageLayout, getStorageValue } from '@cerc-io/solidity-mapper';
|
||||
import {
|
||||
Indexer as BaseIndexer,
|
||||
IndexerInterface,
|
||||
@@ -270,11 +270,14 @@ export class Indexer implements IndexerInterface {
|
||||
defaultValue: any
|
||||
): Promise<Entity> {
|
||||
const [{ number }, syncStatus] = await Promise.all([
|
||||
this._ethProvider.send('eth_getHeaderByHash', [blockHash]),
|
||||
// Laconicd doesn't support eth_getHeaderByHash
|
||||
// this._ethProvider.send('eth_getHeaderByHash', [blockHash]),
|
||||
this._ethProvider.getBlock(blockHash),
|
||||
this.getSyncStatus()
|
||||
]);
|
||||
|
||||
const blockNumber = ethers.BigNumber.from(number).toNumber();
|
||||
// const blockNumber = ethers.BigNumber.from(number).toNumber();
|
||||
const blockNumber = number;
|
||||
|
||||
let result: ValueResult = {
|
||||
value: defaultValue
|
||||
@@ -294,7 +297,17 @@ export class Indexer implements IndexerInterface {
|
||||
const storageLayout = this._storageLayoutMap.get(KIND_PHISHERREGISTRY);
|
||||
assert(storageLayout);
|
||||
|
||||
result = await this._baseIndexer.getStorageValue(
|
||||
// Get storage value using ipld-eth-server
|
||||
// result = await this._baseIndexer.getStorageValue(
|
||||
// storageLayout,
|
||||
// blockHash,
|
||||
// contractAddress,
|
||||
// storageVariableName,
|
||||
// ...Object.values(mappingKeys)
|
||||
// );
|
||||
|
||||
// Get storage value using ETH RPC endpoint
|
||||
result = await this._getStorageValueRPC(
|
||||
storageLayout,
|
||||
blockHash,
|
||||
contractAddress,
|
||||
@@ -313,6 +326,30 @@ export class Indexer implements IndexerInterface {
|
||||
} as any;
|
||||
}
|
||||
|
||||
async _getStorageValueRPC (storageLayout: StorageLayout, blockHash: string, contractAddress: string, variable: string, ...mappingKeys: MappingKey[]): Promise<ValueResult> {
|
||||
const getStorageAt = async (params: { blockHash: string, contract: string, slot: string }) => {
|
||||
const { blockHash, contract, slot } = params;
|
||||
const value = await this._ethProvider.getStorageAt(contract, slot, blockHash);
|
||||
|
||||
return {
|
||||
value,
|
||||
proof: {
|
||||
// Returning null value as proof, since ethers library getStorageAt method doesn't return proof.
|
||||
data: JSON.stringify(null)
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
return getStorageValue(
|
||||
storageLayout,
|
||||
getStorageAt,
|
||||
blockHash,
|
||||
contractAddress,
|
||||
variable,
|
||||
...mappingKeys
|
||||
);
|
||||
}
|
||||
|
||||
async getStorageValue (storageLayout: StorageLayout, blockHash: string, contractAddress: string, variable: string, ...mappingKeys: MappingKey[]): Promise<ValueResult> {
|
||||
return this._baseIndexer.getStorageValue(
|
||||
storageLayout,
|
||||
@@ -608,11 +645,19 @@ export class Indexer implements IndexerInterface {
|
||||
return this._baseIndexer.getAncestorAtDepth(blockHash, depth);
|
||||
}
|
||||
|
||||
// Get latest block using eth client.
|
||||
// Get latest block using eth provider.
|
||||
async getLatestBlock (): Promise<BlockHeight> {
|
||||
const { block } = await this._ethClient.getBlockByHash();
|
||||
// Use ipld-eth-server
|
||||
// const { block } = await this._ethClient.getBlockByHash();
|
||||
|
||||
return block;
|
||||
// Use ETH RPC endpoint
|
||||
const number = await this._ethProvider.getBlockNumber();
|
||||
const { hash } = await this._ethProvider.getBlock(number);
|
||||
|
||||
return {
|
||||
number,
|
||||
hash
|
||||
};
|
||||
}
|
||||
|
||||
// Get full transaction data.
|
||||
|
||||
@@ -3,10 +3,13 @@
|
||||
//
|
||||
|
||||
import debug from 'debug';
|
||||
import { ethers } from 'ethers';
|
||||
import { ethers, Signer } from 'ethers';
|
||||
import { TransactionReceipt, TransactionResponse } from '@ethersproject/providers';
|
||||
|
||||
import { abi as PhisherRegistryABI } from './artifacts/PhisherRegistry.json';
|
||||
|
||||
const log = debug('laconic:libp2p-utils');
|
||||
|
||||
const contractInterface = new ethers.utils.Interface(PhisherRegistryABI);
|
||||
|
||||
const MESSAGE_KINDS = {
|
||||
@@ -14,6 +17,71 @@ const MESSAGE_KINDS = {
|
||||
REVOKE: 'revoke'
|
||||
};
|
||||
|
||||
export async function sendMessageToL2 (
|
||||
signer: Signer,
|
||||
{ contractAddress, gasLimit }: {
|
||||
contractAddress: string,
|
||||
gasLimit: number
|
||||
},
|
||||
data: any
|
||||
): Promise<void> {
|
||||
const { kind, message } = data;
|
||||
const contract = new ethers.Contract(contractAddress, PhisherRegistryABI, signer);
|
||||
let receipt: TransactionReceipt | undefined;
|
||||
|
||||
try {
|
||||
switch (kind) {
|
||||
case MESSAGE_KINDS.INVOKE: {
|
||||
const signedInvocations = message;
|
||||
|
||||
const transaction: TransactionResponse = await contract.invoke(
|
||||
signedInvocations,
|
||||
// Setting gasLimit as eth_estimateGas call takes too long in L2 chain
|
||||
{ gasLimit }
|
||||
);
|
||||
|
||||
receipt = await transaction.wait();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MESSAGE_KINDS.REVOKE: {
|
||||
const { signedDelegation, signedIntendedRevocation } = message;
|
||||
|
||||
const transaction: TransactionResponse = await contract.revokeDelegation(
|
||||
signedDelegation,
|
||||
signedIntendedRevocation,
|
||||
// Setting gasLimit as eth_estimateGas call takes too long in L2 chain
|
||||
{ gasLimit }
|
||||
);
|
||||
|
||||
receipt = await transaction.wait();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
log(`Handler for libp2p message kind ${kind} not implemented`);
|
||||
log(JSON.stringify(message, null, 2));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (receipt) {
|
||||
log(`Transaction receipt for ${kind} message`, {
|
||||
to: receipt.to,
|
||||
blockNumber: receipt.blockNumber,
|
||||
blockHash: receipt.blockHash,
|
||||
transactionHash: receipt.transactionHash,
|
||||
effectiveGasPrice: receipt.effectiveGasPrice.toString(),
|
||||
gasUsed: receipt.gasUsed.toString()
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
log(error);
|
||||
}
|
||||
}
|
||||
|
||||
export function parseLibp2pMessage (log: debug.Debugger, peerId: string, data: any): void {
|
||||
log('Received a message on mobymask P2P network from peer:', peerId);
|
||||
const { kind, message } = data;
|
||||
@@ -57,17 +125,5 @@ function _parseRevocation (log: debug.Debugger, msg: any): void {
|
||||
log('Signed delegation:');
|
||||
log(JSON.stringify(signedDelegation, null, 2));
|
||||
log('Signed intention to revoke:');
|
||||
const stringifiedSignedIntendedRevocation = JSON.stringify(
|
||||
signedIntendedRevocation,
|
||||
(key, value) => {
|
||||
if (key === 'delegationHash' && value.type === 'Buffer') {
|
||||
// Show hex value for delegationHash instead of Buffer
|
||||
return ethers.utils.hexlify(Buffer.from(value));
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
2
|
||||
);
|
||||
log(stringifiedSignedIntendedRevocation);
|
||||
log(JSON.stringify(signedIntendedRevocation, null, 2));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import debug from 'debug';
|
||||
import { hideBin } from 'yargs/helpers';
|
||||
import yargs from 'yargs';
|
||||
import assert from 'assert';
|
||||
|
||||
import { Config, DEFAULT_CONFIG_PATH, getConfig, initClients } from '@cerc-io/util';
|
||||
import {
|
||||
PeerInitConfig,
|
||||
PeerIdObj
|
||||
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1319854183
|
||||
} from '@cerc-io/peer';
|
||||
|
||||
import { sendMessageToL2 } from './libp2p-utils';
|
||||
import { readPeerId } from '@cerc-io/cli';
|
||||
import { ethers } from 'ethers';
|
||||
|
||||
const log = debug('vulcanize:peer-listener');
|
||||
|
||||
const DEFAULT_GAS_LIMIT = 500000;
|
||||
|
||||
interface Arguments {
|
||||
configFile: string;
|
||||
privateKey: string;
|
||||
contractAddress: string;
|
||||
gasLimit: number;
|
||||
}
|
||||
|
||||
export const main = async (): Promise<any> => {
|
||||
const argv = _getArgv();
|
||||
const config: Config = await getConfig(argv.configFile);
|
||||
const { ethProvider } = await initClients(config);
|
||||
|
||||
const p2pConfig = config.server.p2p;
|
||||
const peerConfig = p2pConfig.peer;
|
||||
assert(peerConfig, 'Peer config not set');
|
||||
|
||||
const { Peer } = await import('@cerc-io/peer');
|
||||
|
||||
let peerIdObj: PeerIdObj | undefined;
|
||||
if (peerConfig.peerIdFile) {
|
||||
peerIdObj = readPeerId(peerConfig.peerIdFile);
|
||||
}
|
||||
|
||||
const peer = new Peer(peerConfig.relayMultiaddr, true);
|
||||
|
||||
const peerNodeInit: PeerInitConfig = {
|
||||
pingInterval: peerConfig.pingInterval,
|
||||
pingTimeout: peerConfig.pingTimeout,
|
||||
maxRelayConnections: peerConfig.maxRelayConnections,
|
||||
relayRedialInterval: peerConfig.relayRedialInterval,
|
||||
maxConnections: peerConfig.maxConnections,
|
||||
dialTimeout: peerConfig.dialTimeout,
|
||||
enableDebugInfo: peerConfig.enableDebugInfo
|
||||
};
|
||||
|
||||
await peer.init(peerNodeInit, peerIdObj);
|
||||
const wallet = new ethers.Wallet(argv.privateKey, ethProvider);
|
||||
|
||||
peer.subscribeTopic(peerConfig.pubSubTopic, (peerId, data) => {
|
||||
log('Received a message on mobymask P2P network from peer:', peerId);
|
||||
|
||||
// TODO: throttle message handler
|
||||
sendMessageToL2(wallet, argv, data);
|
||||
});
|
||||
|
||||
log(`Peer ID: ${peer.peerId?.toString()}`);
|
||||
};
|
||||
|
||||
const _getArgv = (): Arguments => {
|
||||
return yargs(hideBin(process.argv)).parserConfiguration({
|
||||
'parse-numbers': false
|
||||
}).options({
|
||||
configFile: {
|
||||
alias: 'config-file',
|
||||
describe: 'configuration file path (toml)',
|
||||
type: 'string',
|
||||
default: DEFAULT_CONFIG_PATH
|
||||
},
|
||||
privateKey: {
|
||||
alias: 'private-key',
|
||||
demandOption: true,
|
||||
describe: 'Private key of the account to use for eth_call',
|
||||
type: 'string'
|
||||
},
|
||||
contractAddress: {
|
||||
alias: 'contract',
|
||||
demandOption: true,
|
||||
describe: 'Address of MobyMask contract',
|
||||
type: 'string'
|
||||
},
|
||||
gasLimit: {
|
||||
alias: 'gas-limit',
|
||||
describe: 'Gas limit for eth txs',
|
||||
type: 'number',
|
||||
default: DEFAULT_GAS_LIMIT
|
||||
}
|
||||
// https://github.com/yargs/yargs/blob/main/docs/typescript.md?plain=1#L83
|
||||
}).parseSync();
|
||||
};
|
||||
|
||||
main().then(() => {
|
||||
log('Starting peer...');
|
||||
}).catch(err => {
|
||||
log(err);
|
||||
});
|
||||
Reference in New Issue
Block a user