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

feat: create job crawl pubkey evm #912

Merged
merged 2 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions ci/config.json.ci
Original file line number Diff line number Diff line change
Expand Up @@ -516,5 +516,10 @@
"chunkSize": 1000,
"blocksPerCall": 100
}
},
"crawlEvmAccountPubkey": {
"key": "crawlEvmAccountPubkey",
"millisecondCrawl": 1000,
"blocksPerCall": 100
}
}
5 changes: 5 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -515,5 +515,10 @@
"chunkSize": 1000,
"blocksPerCall": 100
}
},
"crawlEvmAccountPubkey": {
"key": "crawlEvmAccountPubkey",
"millisecondCrawl": 1000,
"blocksPerCall": 100
}
}
6 changes: 6 additions & 0 deletions src/common/utils/db_connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ export async function batchUpdate(
}
return 'NULL::timestamp';
}
if (record[field]?.type === 'jsonb') {
if (record[field].value !== undefined) {
return `'${record[field].value}'::jsonb`;
}
return '{}';
}

if (record[field] !== undefined) {
return `'${record[field]}'`;
Expand Down
1 change: 1 addition & 0 deletions src/services/evm/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ export const BULL_JOB_NAME = {
HANDLE_SELF_DESTRUCT: 'handle:self-destruct',
REINDEX_ERC20: 'reindex:erc20',
REFRESH_ERC721_HOLDER_STATISTIC: 'refresh:erc721-holder-statistic',
CRAWL_EVM_ACCOUNT_PUBKEY: 'crawl:evm-account-pubkey',
};

export const MSG_TYPE = {
Expand Down
122 changes: 120 additions & 2 deletions src/services/evm/crawl_evm_account.service.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable no-await-in-loop */
import {
Action,
Expand All @@ -6,16 +7,23 @@ import {
import { Knex } from 'knex';
import _, { Dictionary } from 'lodash';
import { Context } from 'moleculer';
import { bytesToHex, PublicClient } from 'viem';
import {
bytesToHex,
PublicClient,
recoverAddress,
recoverPublicKey,
} from 'viem';
import { ethers } from 'ethers';
import config from '../../../config.json' assert { type: 'json' };
import '../../../fetch-polyfill.js';
import BullableService, { QueueHandler } from '../../base/bullable.service';
import knex from '../../common/utils/db_connection';
import knex, { batchUpdate } from '../../common/utils/db_connection';
import { getViemClient } from '../../common/utils/etherjs_client';
import {
Account,
AccountBalance,
BlockCheckpoint,
EVMBlock,
EvmInternalTransaction,
EVMTransaction,
} from '../../models';
Expand Down Expand Up @@ -224,6 +232,102 @@ export default class CrawlEvmAccountService extends BullableService {
];
}

@QueueHandler({
queueName: BULL_JOB_NAME.CRAWL_EVM_ACCOUNT_PUBKEY,
jobName: BULL_JOB_NAME.CRAWL_EVM_ACCOUNT_PUBKEY,
})
async crawlEvmAccountPubkey() {
const [startBlock, endBlock, blockCheckpoint] =
await BlockCheckpoint.getCheckpoint(
BULL_JOB_NAME.CRAWL_EVM_ACCOUNT_PUBKEY,
[BULL_JOB_NAME.CRAWL_EVM_ACCOUNT],
config.crawlEvmAccountPubkey.key
);
this.logger.info(
`Crawl evm account pubkey from block ${startBlock} to ${endBlock}`
);
if (startBlock >= endBlock) {
return;
}
const listBlock = await EVMBlock.query()
.select('transactions')
.where('height', '>', startBlock)
.andWhere('height', '<=', endBlock);
const listAccountDict: any = {};
await Promise.all(
listBlock.map(async (block) => {
const listTxs = block.transactions;
await Promise.all(
listTxs.map(async (tx: any) => {
const txData = {
gasPrice: tx.gasPrice,
gasLimit: tx.gas,
value: tx.value,
nonce: tx.nonce,
data: tx.input,
chainId: tx.chainId,
type: Number(tx.typeHex),
maxFeePerGas: tx.maxFeePerGas,
maxPriorityFeePerGas: tx.maxPriorityFeePerGas,
to: tx.to,
};
const txs = (await ethers.resolveProperties(txData)) as any;
const rawTx = ethers.Transaction.from(txs).unsignedSerialized; // returns RLP encoded tx
const sig = {
r: tx.r,
s: tx.s,
v: tx.v,
};
const signature = ethers.Signature.from(sig).serialized as any;
const msgHash = ethers.keccak256(rawTx); // as specified by ECDSA
const msgBytes = ethers.getBytes(msgHash); // create binary hash
const recoveredPubKey = await recoverPublicKey({
hash: msgBytes,
signature,
});

const recoveredAddress = await recoverAddress({
hash: msgBytes,
signature,
});
if (tx.from !== recoveredAddress.toLowerCase()) {
this.logger.error(`cannot recover address at ${tx.hash}`);
throw Error(`cannot recover address at ${tx.hash}`);
}
listAccountDict[tx.from] = {
pubkey: JSON.stringify({
pubkeyEVM: recoveredPubKey,
}),
};
})
);
})
);

await knex.transaction(async (trx) => {
if (Object.keys(listAccountDict).length > 0) {
const listAccountDB = await Account.query().whereIn(
'address',
Object.keys(listAccountDict)
);
const listAccount = Object.keys(listAccountDict).map((e) => ({
account: e,
pubkey: { type: 'jsonb', value: listAccountDict[e].pubkey },
id: listAccountDB.find((a) => a.evm_address === e)?.id,
}));
await batchUpdate(trx, 'account', listAccount, ['pubkey']);
}
if (blockCheckpoint) {
blockCheckpoint.height = endBlock;
await BlockCheckpoint.query()
.insert(blockCheckpoint)
.onConflict('job_name')
.merge()
.transacting(trx);
}
});
}

public async _start(): Promise<void> {
this.viemClient = getViemClient();
await this.createJob(
Expand All @@ -240,6 +344,20 @@ export default class CrawlEvmAccountService extends BullableService {
},
}
);
await this.createJob(
BULL_JOB_NAME.CRAWL_EVM_ACCOUNT_PUBKEY,
BULL_JOB_NAME.CRAWL_EVM_ACCOUNT_PUBKEY,
{},
{
removeOnComplete: true,
removeOnFail: {
count: 3,
},
repeat: {
every: config.crawlEvmAccountPubkey.millisecondCrawl,
},
}
);
return super._start();
}
}
Loading