diff --git a/src/index.ts b/src/index.ts index 6698fd5..6b498ce 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,43 +1,23 @@ -import { bytesToHex, formatReceipt, getEnv } from './utils.js'; +import { getEnv } from './utils.js'; import { EthAPI } from './eth.js'; import { LightClientAPI } from './lightclient.js'; -import { TrieWrapper} from './triewrapper.js'; -import { Receipt, UserInput } from './types.js'; -import assert from 'assert'; +import { UserInput } from './types.js'; -const CONTRACT_ADDRESS = getEnv("CONTRACT_ADDRESS") - -async function isTopicInTransaction(api: EthAPI, lightClient: LightClientAPI, input: UserInput) { - const block = await api.getBlock(input.blockNumber); - - const receipts: Receipt[] = (await api.getBlockTransactionReceipts(input.blockNumber)).map(formatReceipt); - const requestedTxReceipt = receipts.find((r) => r.transactionHash === input.transactionHash); - if (!requestedTxReceipt) throw new Error(`Could not find receipt for transaction ${input.transactionHash} in block ${input.blockNumber}`); - - const transactionIndex = parseInt(requestedTxReceipt.transactionIndex, 16); - const receiptsTrie = await TrieWrapper.trieFromReceipts(receipts); - assert( - block.receiptsRoot.slice(2) === bytesToHex(receiptsTrie.root()), - `Expected receipts root (${block.receiptsRoot}) doesn't match the actual (${bytesToHex(receiptsTrie.root())})`); - const proof = await TrieWrapper.createProof(receiptsTrie, transactionIndex); - - const isValidReceipt = true; - // const isValidReceipt = await lightClient.verify_receipt( - // block.receiptsRoot.slice(2), - // proof.map((p) => bytesToHex(p)), - // TrieWrapper.encodeKey(transactionIndex) - // ); - - if (isValidReceipt) { - return requestedTxReceipt.logs.find(log => log[1].includes(input.topic)) !== undefined; - } -} +const CONTRACT_ADDRESS = getEnv("CONTRACT_ADDRESS") const main = async () => { const api = new EthAPI() const lightClient = new LightClientAPI(CONTRACT_ADDRESS); await lightClient.init() + const input: UserInput = { + blockNumber: 17875570, + transactionHash: '0x022edd6e5e56c918b0ec5177ec41569e606957af7d16d9e9e65174ed522830dc', + topic: '0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196' + }; + + console.log(await lightClient.verifyTopicInTransaction(api, input)); + // const t = await api.getBeaconBlock(7061552); // const update = await api.getUpdate(864); // await lightClient.applyNewUpdate(update, 864); diff --git a/src/lightclient.ts b/src/lightclient.ts index 7782f13..4d49886 100644 --- a/src/lightclient.ts +++ b/src/lightclient.ts @@ -1,8 +1,12 @@ import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing' -import { getEnv } from './utils.js' +import { bytesToHex, formatReceipt, getEnv } from './utils.js' import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate" -import { GasPrice } from '@cosmjs/stargate' +import { GasPrice, SearchByHeightQuery, SearchTxQuery } from '@cosmjs/stargate' import * as capella from '@lodestar/types/capella' +import { Receipt, UserInput } from './types.js' +import { EthAPI } from './eth.js' +import { TrieWrapper } from './triewrapper.js' +import assert from 'assert'; export class LightClientAPI { private myAddress: string @@ -10,7 +14,7 @@ export class LightClientAPI { constructor( private address: string, - private mnemonic = getEnv("MNEMONIC"), + private mnemonic = getEnv("MNEMONIC"), private rpcUrl = getEnv("AXELAR_RPC_URL") ) {} @@ -33,10 +37,10 @@ export class LightClientAPI { console.log("Error applying update for period", period, e) return false } - + const contractPeriod = await this.getPeriod(); console.log("Current contract period after update", contractPeriod) - + return true } @@ -45,6 +49,14 @@ export class LightClientAPI { return Math.floor(res.finalized_header.slot / 32 / 256) } + async verify_proof(msg: any): Promise { + return this.execute(msg); + } + + async verify_topic_inclusion(msg: any): Promise { + return this.execute(msg); + } + private async query(msg: any): Promise { return await this.client.queryContractSmart(this.address, msg) } @@ -52,4 +64,48 @@ export class LightClientAPI { private async execute(msg: any): Promise { return await this.client.execute(this.myAddress, this.address, msg, 'auto') } + + async verifyTopicInTransaction(api: EthAPI, input: UserInput): Promise { + const block = await api.getBlock(input.blockNumber); + + const receipts: Receipt[] = (await api.getBlockTransactionReceipts(input.blockNumber)).map(formatReceipt); + const requestedTxReceipt = receipts.find((r) => r.transactionHash === input.transactionHash); + if (!requestedTxReceipt) throw new Error(`Could not find receipt for transaction ${input.transactionHash} in block ${input.blockNumber}`); + + const transactionIndex = parseInt(requestedTxReceipt.transactionIndex, 16); + const receiptsTrie = await TrieWrapper.trieFromReceipts(receipts); + assert( + block.receiptsRoot.slice(2) === bytesToHex(receiptsTrie.root()), + `Expected receipts root (${block.receiptsRoot}) doesn't match the actual (${bytesToHex(receiptsTrie.root())})`); + const proof = await TrieWrapper.createProof(receiptsTrie, transactionIndex); + + const response = await this.verify_proof({ + VerifyProof: { + proof: proof.map(n => `0x${Buffer.from(n).toString('hex')}`), + key: Buffer.from(TrieWrapper.encodeKey(transactionIndex)).toString('hex'), + root: block.receiptsRoot + } + }); + // wanted: 276233 + // used: 211619 + + const receiptEncoded = response.events + .find((event: any) => + event.type === 'wasm' && + event.attributes.find((a: any) => a.key === 'result') + )?.attributes.find((a: any) => a.key === 'result')?.value; + if (parseInt(receiptEncoded, 16) !== 0) { + const response = await this.verify_topic_inclusion({ + VerifyTopicInclusion: { + receipt: `0x${receiptEncoded}`, + topic: input.topic, + } + }); + // "gasWanted": 169275, + // "gasUsed": 135219 + return response.events.find((e: any) => e.type === 'wasm').attributes.find((a: any) => a.key === 'result').value === 'true'; + } else { + return false; + } + } } \ No newline at end of file