From 2281a4663fe00d43d61e21bc9682ae8897a714af Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Sat, 1 Jun 2024 11:41:24 +0200 Subject: [PATCH 01/11] init --- .vscode/settings.json | 3 + package-lock.json | 6 + package.json | 1 + src/api/extension/index.js | 21 ++- src/config/provider.js | 12 ++ src/ui/app/components/historyViewer.jsx | 240 ++++++++++++++++++++++++ src/ui/app/components/transaction.jsx | 54 ++++++ 7 files changed, 332 insertions(+), 5 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..ae6cc38f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "cSpell.words": ["bech", "mithril", "multiasset"] +} diff --git a/package-lock.json b/package-lock.json index 20085852..ef8ace76 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "@emurgo/cip14-js": "^3.0.1", "@fontsource/ubuntu": "^5.0.8", "@ledgerhq/hw-transport-webusb": "^6.28.0", + "@mithril-dev/mithril-client-wasm": "^0.3.0", "@trezor/connect-web": "^9.0.11", "bip39": "^3.0.4", "crc": "^4.1.1", @@ -4570,6 +4571,11 @@ "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", "dev": true }, + "node_modules/@mithril-dev/mithril-client-wasm": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@mithril-dev/mithril-client-wasm/-/mithril-client-wasm-0.3.0.tgz", + "integrity": "sha512-Je3KQGyze8lUtXzE1I544kT4FKBBwe4CK7GUeV07n5+9ILoGnWv10KFTnNRUFCBX3GcP5/vMG/9RVr0EB/tTfg==" + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", diff --git a/package.json b/package.json index c6923fa4..e1c9e391 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@emurgo/cip14-js": "^3.0.1", "@fontsource/ubuntu": "^5.0.8", "@ledgerhq/hw-transport-webusb": "^6.28.0", + "@mithril-dev/mithril-client-wasm": "^0.3.0", "@trezor/connect-web": "^9.0.11", "bip39": "^3.0.4", "crc": "^4.1.1", diff --git a/src/api/extension/index.js b/src/api/extension/index.js index 6e151167..8c52c28e 100644 --- a/src/api/extension/index.js +++ b/src/api/extension/index.js @@ -144,6 +144,14 @@ export const getDelegation = async () => { }; }; +export const getTxCBOR = async (txHash) => { + // TODO: Get real CBOR + // const result = await blockfrostRequest(`/txs/${txHash}/cbor`); + // if (!result || result.error) return null; + // return result; + return '84a40081825820203e5b61e0949ffc8fe594727cf7ed73c7396cc2bd212af9a680c9423b5880eb00018282583900f0c60254ecb0addd4c7e40c28fd05b65014ab4c8ecece06c7dcee5a0724bf93336a8225e7ef152b41aea955173be91af19250edea1ddafab1a000f42408258390014beadb876d0a2a593fe2f1b539389e00731290910170e9a1be78e847d2ccdc7af469706878018739bcfde9ae23f009c4ae38aee0a4b4f3a1b0000000253fa0f93021a0002922d031a0303c827a100818258207d3ae39f9a1c916ac7c13f10c7d67c70b870c286a1af71485455c5022a3f391d5840e2f481acd1601a3f39fa976317bba685ddd774621a92611edaaa3df9f48a3b13d8b25ecb2f28b031c1602512418efed3033e463a0dcd22a856c808033cc9e00ff5f6'; +}; + export const getPoolMetadata = async (poolId) => { if (!poolId) { throw new Error('poolId argument not provided'); @@ -1558,12 +1566,12 @@ export const getAdaHandle = async (assetName) => { const network = await getNetwork(); if (!network) return null; let handleUrl; - switch (network.id){ + switch (network.id) { case 'mainnet': - handleUrl = 'https://api.handle.me' + handleUrl = 'https://api.handle.me'; break; case 'preprod': - handleUrl = 'https://preprod.api.handle.me' + handleUrl = 'https://preprod.api.handle.me'; break; default: return null; @@ -1769,7 +1777,9 @@ export const getAsset = async (unit) => { const metadata = metadataDatum && Data.toJson(metadataDatum.fields[0]); asset.displayName = metadata.name; - asset.image = metadata.image ? linkToSrc(convertMetadataPropToString(metadata.image)) : ''; + asset.image = metadata.image + ? linkToSrc(convertMetadataPropToString(metadata.image)) + : ''; asset.decimals = 0; } catch (_e) { asset.displayName = asset.name; @@ -1796,7 +1806,8 @@ export const getAsset = async (unit) => { const metadata = metadataDatum && Data.toJson(metadataDatum.fields[0]); asset.displayName = metadata.name; - asset.image = linkToSrc(convertMetadataPropToString(metadata.logo)) || ''; + asset.image = + linkToSrc(convertMetadataPropToString(metadata.logo)) || ''; asset.decimals = metadata.decimals || 0; } catch (_e) { asset.displayName = asset.name; diff --git a/src/config/provider.js b/src/config/provider.js index 67b9860c..fa1fe8fe 100644 --- a/src/config/provider.js +++ b/src/config/provider.js @@ -23,5 +23,17 @@ export default { ) .then((res) => res.json()) .then((res) => res.cardano[currency]), + mithril: (network) => { + const mithrilBaseURL = new URL('http://localhost:3000'); + // TODO: Mithril-client does not support URL with credentials, + // nor there is any possibility to inject project_id header + // const mithrilBaseURL = new URL(provider.api.base(network.node)); + // mithrilBaseURL.username = 'mithril'; + // mithrilBaseURL.password = networkToProjectId[network.name]; + mithrilBaseURL.pathname = mithrilBaseURL.pathname + 'mithril'; + + console.log('mithrilBaseURL.href', mithrilBaseURL.href); + return mithrilBaseURL.href; + }, }, }; diff --git a/src/ui/app/components/historyViewer.jsx b/src/ui/app/components/historyViewer.jsx index 17a95d91..6bd8e4de 100644 --- a/src/ui/app/components/historyViewer.jsx +++ b/src/ui/app/components/historyViewer.jsx @@ -1,15 +1,230 @@ import { Box, Text, Spinner, Accordion, Button } from '@chakra-ui/react'; import { ChevronDownIcon } from '@chakra-ui/icons'; import React from 'react'; +import Loader from '../../../api/loader'; import { File } from 'react-kawaii'; import { + getNetwork, getTransactions, + getTxCBOR, setTransactions, setTxDetail, } from '../../../api/extension'; import Transaction from './transaction'; import { useCaptureEvent } from '../../../features/analytics/hooks'; import { Events } from '../../../features/analytics/events'; +import initMithrilClient, { + MithrilClient, +} from '@mithril-dev/mithril-client-wasm'; +import provider from '../../../config/provider'; +import { output } from '../../../../webpack.config'; + +// let aggregator_endpoint = +// 'http://mithril:mainnetjTvHPJCsB63oTESdYcua2ZhKTECveeIG@localhost:4000/backend/mithril'; + +const network = getNetwork(); +let aggregator_endpoint = provider.api.mithril(network); + +let genesis_verification_key = + '5b3132372c37332c3132342c3136312c362c3133372c3133312c3231332c3230372c3131372c3139382c38352c3137362c3139392c3136322c3234312c36382c3132332c3131392c3134352c31332c3233322c3234332c34392c3232392c322c3234392c3230352c3230352c33392c3233352c34345d'; + +// const broadcast_channel = new BroadcastChannel('mithril-client'); +// broadcast_channel.onmessage = (e) => { +// let event = e.data; +// if (event.type == 'CertificateChainValidationStarted') { +// console.log('The certificate chain validation has started'); +// } else if (event.type == 'CertificateValidated') { +// console.log( +// 'A certificate has been validated, certificate_hash: ' + +// event.payload.certificate_hash +// ); +// } else if (event.type == 'CertificateChainValidated') { +// console.log('The certificate chain is valid'); +// } else { +// console.log(event); +// } +// }; + +const getTxsCerts = async (txHashes) => { + await initMithrilClient(); + + let client = await new MithrilClient( + aggregator_endpoint, + genesis_verification_key + ); + const proof = await client.unstable.get_cardano_transaction_proofs(txHashes); + + console.log('Proof', proof); + console.log('Proof.transactions_hashes', proof.transactions_hashes); + + let proof_certificate = await client.verify_certificate_chain( + proof.certificate_hash + ); + console.log( + 'verify_certificate_chain OK, last_certificate_from_chain:', + proof_certificate + ); + + let valid_cardano_transaction_proof = + await client.unstable.verify_cardano_transaction_proof_then_compute_message( + proof, + proof_certificate + ); + console.log( + 'valid_cardano_transaction_proof:', + valid_cardano_transaction_proof + ); + + return proof; +}; + +export const multiAssetToArray = (multiAsset) => { + if (!multiAsset) return []; + const assetsArray = []; + const policyHashes = multiAsset.keys(); + + for (let i = 0; i < policyHashes.len(); i++) { + const policyId = policyHashes.get(i); + const assetsInPolicy = multiAsset.get(policyId); + if (!assetsInPolicy) continue; + + const assetNames = assetsInPolicy.keys(); + for (let j = 0; j < assetNames.len(); j++) { + const assetName = assetNames.get(j); + const amount = assetsInPolicy.get(assetName); + if (!amount) continue; + + const policyIdHex = Buffer.from(policyId.to_bytes()).toString('hex'); + const assetNameHex = Buffer.from(assetName.name()).toString('hex'); + + assetsArray.push({ + quantity: amount.to_str(), + unit: `${policyIdHex}${assetNameHex}`, + }); + } + } + return assetsArray; +}; + +const verifyCBORData = async (txHashes, history) => { + const verifiedTxHashes = []; + await Loader.load(); + + const Cardano = Loader.Cardano; + for (const txHash of txHashes) { + const txData = history.details[txHash]; + const txCBOR = await getTxCBOR(txHash); + + console.log(`Verifying ${txHash}...`); + + if (!txData) { + continue; + } + + // Note: Computing tx hash using old CML that is included in Nami is flawed. + // The computed tx hash may be different than the original despite the transaction being the same + // CSL provides a way to safely compute original tx hash: + // https://github.com/Emurgo/cardano-serialization-lib/issues/604 + // const tx = Cardano.FixedTransaction.from_hex(txCBOR); + // const computedTxHash = Cardano.TransactionHash.from_bytes(blake2b(32).update(tx.raw_body()).digest('binary')); + + const tx = Cardano.Transaction.from_bytes(Buffer.from(txCBOR, 'hex')); + const txBody = tx.body(); + + // Verify that received tx hash matches the received CBOR data + const computedTxHash = Buffer.from( + Cardano.hash_transaction(txBody).to_bytes() + ).toString('hex'); + + if (txHash !== computedTxHash) { + console.log( + `Computed tx hash ${computedTxHash} does not match Blockfrost JSON data ${txHash}` + ); + } + + const { inputs, outputs } = txData.utxos; + + if (txBody.outputs().len() !== outputs.length) { + console.log( + `CBOR verification failed for tx ${txHash}. Outputs length mismatch (CBOR: ${txBody + .outputs() + .len()}, JSON: ${outputs.length})` + ); + continue; + } + + // Compare tx inputs + for (let i = 0; i < inputs.length; i++) { + const cborInput = txBody.inputs().get(i); + const jsonInput = inputs[i]; + if (cborInput.transaction_id().to_hex() !== jsonInput.tx_hash) { + console.log( + `Tx input ${jsonInput.txHash} mismatch (CBOR: ${cborInput + .transaction_id() + .to_hex()} JSON: ${jsonInput.txHash})` + ); + continue; + } + + if (cborInput.index().to_str() !== jsonInput.output_index.toString()) { + console.log( + `Tx input ${ + jsonInput.txHash + } index mismatch (CBOR: ${cborInput.index()} JSON: ${ + jsonInput.output_index + })` + ); + continue; + } + } + + // Compare tx outputs + for (let i = 0; i < outputs.length; i++) { + const cborOutput = txBody.outputs().get(i); + const jsonOutput = outputs[i]; + // lovelace amount + + const jsonLovelaceAMount = jsonOutput.amount.find( + (a) => a.unit === 'lovelace' + )?.quantity; + if (cborOutput.amount().coin().to_str() !== jsonLovelaceAMount) { + console.log( + `amounts do not match. CBOR: ${cborOutput + .amount() + .coin() + .to_str()}, JSON: ${jsonLovelaceAMount}` + ); + continue; + } + // address + if (cborOutput.address().to_bech32() !== jsonOutput.address) { + console.log(`addresses do not match`); + continue; + } + + // compare assets + const cborAssets = multiAssetToArray(cborOutput.amount().multiasset()); + for (const asset of cborAssets) { + const receivedAmount = jsonOutput.amount.find( + (unit) => unit === asset.unit + ).amount; + const amountMatch = receivedAmount === asset.quantity; + if (!amountMatch) { + console.log( + `amount of ${asset.unit} does not match. Received: ${receivedAmount}. Expected: ${asset.quantity}` + ); + } + } + } + + verifiedTxHashes.push(txHash); + } + + console.log(`CBOR verified txs`, verifiedTxHashes); + return { + verifiedTxHashes, + }; +}; const BATCH = 5; @@ -23,6 +238,9 @@ const HistoryViewer = ({ history, network, currentAddr, addresses }) => { const [page, setPage] = React.useState(1); const [final, setFinal] = React.useState(false); const [loadNext, setLoadNext] = React.useState(false); + const [isMithrilLoading, setIsMithrilLoading] = React.useState(false); + const [verificationData, setVerificationData] = React.useState(); + const getTxs = async () => { if (!history) { slice = []; @@ -47,7 +265,9 @@ const HistoryViewer = ({ history, network, currentAddr, addresses }) => { } } if (slice.length < page * BATCH) setFinal(true); + setHistorySlice(slice); + await runMithrilValidation(slice); }; React.useEffect(() => { @@ -73,6 +293,17 @@ const HistoryViewer = ({ history, network, currentAddr, addresses }) => { if (historySlice.length >= (page - 1) * BATCH) setLoadNext(false); }, [historySlice]); + const runMithrilValidation = async (txHashes) => { + setIsMithrilLoading(true); + // Verify that tx hashes matches CBOR data + const cborVerification = await verifyCBORData(txHashes, history); + // Get Mithril proofs + const proof = await getTxsCerts(txHashes); + setVerificationData({ mithril: proof, cbor: cborVerification }); + setIsMithrilLoading(false); + }; + + console.log('verificationData', verificationData); return ( {!(history && historySlice) ? ( @@ -103,12 +334,21 @@ const HistoryViewer = ({ history, network, currentAddr, addresses }) => { > {historySlice.map((txHash, index) => { if (!history.details[txHash]) history.details[txHash] = {}; + const mithrilVerified = + verificationData?.mithril.transactions_hashes.find( + (proofTxHash) => proofTxHash === txHash + ); + const cborVerified = verificationData?.cbor.verifiedTxHashes.find( + (proofTxHash) => proofTxHash === txHash + ); return ( { txObject[txHash] = txDetail; }} + mithrilVerified={mithrilVerified && cborVerified} + isMithrilLoading={isMithrilLoading} key={index} txHash={txHash} detail={history.details[txHash]} diff --git a/src/ui/app/components/transaction.jsx b/src/ui/app/components/transaction.jsx index a23f8526..56feb10f 100644 --- a/src/ui/app/components/transaction.jsx +++ b/src/ui/app/components/transaction.jsx @@ -14,6 +14,7 @@ import { Icon, useColorModeValue, Skeleton, + Spinner, } from '@chakra-ui/react'; import { compileOutputs } from '../../../api/util'; import TimeAgo from 'javascript-time-ago'; @@ -28,6 +29,7 @@ import { NETWORK_ID } from '../../../config/config'; import { useStoreState } from 'easy-peasy'; import { FaCoins, + FaCheckCircle, FaPiggyBank, FaTrashAlt, FaRegEdit, @@ -35,6 +37,7 @@ import { FaUsers, FaRegFileCode, IoRemoveCircleSharp, + IoWarning, TiArrowForward, TiArrowBack, TiArrowShuffle, @@ -91,6 +94,8 @@ const Transaction = ({ addresses, network, onLoad, + mithrilVerified, + isMithrilLoading, }) => { const settings = useStoreState((state) => state.settings.settings); const isMounted = useIsMounted(); @@ -142,6 +147,7 @@ const Transaction = ({ borderRadius={10} borderLeftRadius={30} p={0} + position={'relative'} _hover={{ backgroundColor: colorMode.txBgHover }} _focus={{ border: 'none' }} > @@ -156,6 +162,54 @@ const Transaction = ({ > + +
+ {!isMithrilLoading ? ( + <> + + + {mithrilVerified ? 'Verified' : 'Untrusted'} + + + ) : ( + <> + + + Verifying + + + )} +
+
Date: Fri, 7 Jun 2024 11:46:02 +0200 Subject: [PATCH 02/11] fix: cbor validation --- src/ui/app/components/historyViewer.jsx | 60 ++++++++++++++----------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/src/ui/app/components/historyViewer.jsx b/src/ui/app/components/historyViewer.jsx index 6bd8e4de..12951a11 100644 --- a/src/ui/app/components/historyViewer.jsx +++ b/src/ui/app/components/historyViewer.jsx @@ -19,14 +19,7 @@ import initMithrilClient, { import provider from '../../../config/provider'; import { output } from '../../../../webpack.config'; -// let aggregator_endpoint = -// 'http://mithril:mainnetjTvHPJCsB63oTESdYcua2ZhKTECveeIG@localhost:4000/backend/mithril'; - const network = getNetwork(); -let aggregator_endpoint = provider.api.mithril(network); - -let genesis_verification_key = - '5b3132372c37332c3132342c3136312c362c3133372c3133312c3231332c3230372c3131372c3139382c38352c3137362c3139392c3136322c3234312c36382c3132332c3131392c3134352c31332c3233322c3234332c34392c3232392c322c3234392c3230352c3230352c33392c3233352c34345d'; // const broadcast_channel = new BroadcastChannel('mithril-client'); // broadcast_channel.onmessage = (e) => { @@ -45,7 +38,14 @@ let genesis_verification_key = // } // }; -const getTxsCerts = async (txHashes) => { +const runMithrilVerification = async (txHashes) => { + const genesis_verification_key = + '5b3132372c37332c3132342c3136312c362c3133372c3133312c3231332c3230372c3131372c3139382c38352c3137362c3139392c3136322c3234312c36382c3132332c3131392c3134352c31332c3233322c3234332c34392c3232392c322c3234392c3230352c3230352c33392c3233352c34345d'; + + // let aggregator_endpoint = + // 'http://mithril:mainnetjTvHPJCsB63oTESdYcua2ZhKTECveeIG@localhost:4000/backend/mithril'; + const aggregator_endpoint = provider.api.mithril(network); + await initMithrilClient(); let client = await new MithrilClient( @@ -53,9 +53,7 @@ const getTxsCerts = async (txHashes) => { genesis_verification_key ); const proof = await client.unstable.get_cardano_transaction_proofs(txHashes); - console.log('Proof', proof); - console.log('Proof.transactions_hashes', proof.transactions_hashes); let proof_certificate = await client.verify_certificate_chain( proof.certificate_hash @@ -64,7 +62,6 @@ const getTxsCerts = async (txHashes) => { 'verify_certificate_chain OK, last_certificate_from_chain:', proof_certificate ); - let valid_cardano_transaction_proof = await client.unstable.verify_cardano_transaction_proof_then_compute_message( proof, @@ -120,9 +117,14 @@ const verifyCBORData = async (txHashes, history) => { if (!txData) { continue; } + if (!txData.utxos) { + console.log(`Missing UTXOs for tx ${txHash}`); + continue; + } - // Note: Computing tx hash using old CML that is included in Nami is flawed. + // Note: There is a change that computing tx hash using old CML that is included in Nami is flawed. // The computed tx hash may be different than the original despite the transaction being the same + // due to tx being reconstructed with non-canonical CBOR. // CSL provides a way to safely compute original tx hash: // https://github.com/Emurgo/cardano-serialization-lib/issues/604 // const tx = Cardano.FixedTransaction.from_hex(txCBOR); @@ -144,6 +146,15 @@ const verifyCBORData = async (txHashes, history) => { const { inputs, outputs } = txData.utxos; + if (txBody.inputs().len() !== inputs.length) { + console.log( + `CBOR verification failed for tx ${txHash}. Inputs length mismatch (CBOR: ${txBody + .inputs() + .len()}, JSON: ${inputs.length})` + ); + continue; + } + if (txBody.outputs().len() !== outputs.length) { console.log( `CBOR verification failed for tx ${txHash}. Outputs length mismatch (CBOR: ${txBody @@ -182,8 +193,8 @@ const verifyCBORData = async (txHashes, history) => { for (let i = 0; i < outputs.length; i++) { const cborOutput = txBody.outputs().get(i); const jsonOutput = outputs[i]; - // lovelace amount + // lovelace amount const jsonLovelaceAMount = jsonOutput.amount.find( (a) => a.unit === 'lovelace' )?.quantity; @@ -204,14 +215,14 @@ const verifyCBORData = async (txHashes, history) => { // compare assets const cborAssets = multiAssetToArray(cborOutput.amount().multiasset()); - for (const asset of cborAssets) { - const receivedAmount = jsonOutput.amount.find( - (unit) => unit === asset.unit - ).amount; - const amountMatch = receivedAmount === asset.quantity; + for (const cborAsset of cborAssets) { + const jsonAssetQuantity = jsonOutput.amount.find( + (a) => a.unit === cborAsset.unit + ).quantity; + const amountMatch = jsonAssetQuantity === cborAsset.quantity; if (!amountMatch) { console.log( - `amount of ${asset.unit} does not match. Received: ${receivedAmount}. Expected: ${asset.quantity}` + `amount of ${cborAsset.unit} does not match. Received: ${jsonAssetQuantity}. Expected: ${cborAsset.quantity}` ); } } @@ -267,7 +278,7 @@ const HistoryViewer = ({ history, network, currentAddr, addresses }) => { if (slice.length < page * BATCH) setFinal(true); setHistorySlice(slice); - await runMithrilValidation(slice); + await runTxVerification(slice); }; React.useEffect(() => { @@ -293,17 +304,16 @@ const HistoryViewer = ({ history, network, currentAddr, addresses }) => { if (historySlice.length >= (page - 1) * BATCH) setLoadNext(false); }, [historySlice]); - const runMithrilValidation = async (txHashes) => { + const runTxVerification = async (txHashes) => { setIsMithrilLoading(true); - // Verify that tx hashes matches CBOR data + // Verify that tx JSON data matches CBOR data const cborVerification = await verifyCBORData(txHashes, history); - // Get Mithril proofs - const proof = await getTxsCerts(txHashes); + // Run mithril verification + const proof = await runMithrilVerification(txHashes); setVerificationData({ mithril: proof, cbor: cborVerification }); setIsMithrilLoading(false); }; - console.log('verificationData', verificationData); return ( {!(history && historySlice) ? ( From aac276ba9c4429f5a2211c2429886fbc357b9ef2 Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Fri, 7 Jun 2024 11:46:53 +0200 Subject: [PATCH 03/11] chore: mock tx that hasnt been pushed to chain --- src/api/extension/index.js | 180 +++++++++++++++++++++++++++++++++++-- 1 file changed, 174 insertions(+), 6 deletions(-) diff --git a/src/api/extension/index.js b/src/api/extension/index.js index 8c52c28e..23a0012d 100644 --- a/src/api/extension/index.js +++ b/src/api/extension/index.js @@ -149,7 +149,14 @@ export const getTxCBOR = async (txHash) => { // const result = await blockfrostRequest(`/txs/${txHash}/cbor`); // if (!result || result.error) return null; // return result; - return '84a40081825820203e5b61e0949ffc8fe594727cf7ed73c7396cc2bd212af9a680c9423b5880eb00018282583900f0c60254ecb0addd4c7e40c28fd05b65014ab4c8ecece06c7dcee5a0724bf93336a8225e7ef152b41aea955173be91af19250edea1ddafab1a000f42408258390014beadb876d0a2a593fe2f1b539389e00731290910170e9a1be78e847d2ccdc7af469706878018739bcfde9ae23f009c4ae38aee0a4b4f3a1b0000000253fa0f93021a0002922d031a0303c827a100818258207d3ae39f9a1c916ac7c13f10c7d67c70b870c286a1af71485455c5022a3f391d5840e2f481acd1601a3f39fa976317bba685ddd774621a92611edaaa3df9f48a3b13d8b25ecb2f28b031c1602512418efed3033e463a0dcd22a856c808033cc9e00ff5f6'; + + if ( + txHash === + '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd' + ) { + return '84a4008282582040f5ce4bc6dad1390c0c56b847f860b1fc27c45f1a531e63d16b0a498c21212200825820eadcfeb38de327a0def9ab03f11ca37ca050c7318e96c9bfcc7c51406a584eec01018282583900f0c60254ecb0addd4c7e40c28fd05b65014ab4c8ecece06c7dcee5a0724bf93336a8225e7ef152b41aea955173be91af19250edea1ddafab821a001226b8a1581c61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d935a15046696e616e636542696e617269657334028258390009fe15e51f5109d5ace334448318d378e8ddf69a45c61b1347a200ed7d2ccdc7af469706878018739bcfde9ae23f009c4ae38aee0a4b4f3a821b0000000253f75452a1581c61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d935a15046696e616e636542696e61726965733408021a0002bb41031a0308319aa1008282582015a834ae1b84aecb354d7b56dd9c2153d947328b0f2ae722a7e61b581b25598458404a29dfdcd159e16232f16df270fb25a3584f303dfcc798206069da32345d4f95f588577bb6928aadcb40af77c4198b6572755bca3c673f4debe2ad354bdea50c8258203fefb4a308e15d716883dea41d5f7fb508cbee5557ea585e7b3351c931b72eb6584057bff5ba29a71db4645da8129f279706a45d556b086445687fd11a19e700ea02c5997af1bbd51eba416151097220142763c6f8a7606f8791fb63ce8324ee3308f5f6'; + } + return '84a400818258206dd6cf3fa81222062093885417b5f872f4d5d5ebfcea60de2ba6dc32edf20f0101018382583900f0c60254ecb0addd4c7e40c28fd05b65014ab4c8ecece06c7dcee5a0724bf93336a8225e7ef152b41aea955173be91af19250edea1ddafab821a001226b8a1581c61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d935a15046696e616e636542696e6172696573340a82583900f0c60254ecb0addd4c7e40c28fd05b65014ab4c8ecece06c7dcee5a0724bf93336a8225e7ef152b41aea955173be91af19250edea1ddafab1a001e848082583900e60861fc19a8cb2b9c04b9f9d35b093d7733120851c1e0487386386412671c592938844c1400d65d134135e35b4f29cdc79aa1404a0e1112821a05e8dba6a8581c0d55169aefdbff511ea14a966c0519e0d964c073143201583099cddba14848656c6c6f4e465401581c1f39bdd2257939e0e14d76f7afff2a5bf1ead57224fd10f94c37ae9ea14848656c6c6f4e465401581c3bbd184d7b858623b77f8b203d2af8aff629a7ce72143a9d993e8ee9a14848656c6c6f4e465401581c599d7575d0c0756998c027c5c72461ede01088ca2e919d2b54cfb9afa14848656c6c6f4e465401581c61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d935a95046696e616e636542696e6172696573301a000f42405046696e616e636542696e6172696573321a000f42405046696e616e636542696e6172696573331a000f42405046696e616e636542696e6172696573341a000f42365046696e616e636542696e6172696573351a000f42405046696e616e636542696e6172696573361a000f42405046696e616e636542696e6172696573371a000f42405046696e616e636542696e6172696573381a000f423f5046696e616e636542696e6172696573391a000f4240581c8413eb5387d7cc27d87122c8f6420d95c5773e7cd9fb41c64d99b7dea14848656c6c6f4e465401581c87f310392793674823e9eea90fed63fec31dc2247ab58640b58bb95ea14848656c6c6f4e465401581c8e9d119fcf988b818e1d74361244eb44c886323fc92ed4fea9d46b94a14848656c6c6f4e465401021a0002fe75031a03082880a10081825820106b5fad29bdfe9d6cd55292292bbde7f4fff45adb9473bb186510f54594a80b5840c49f180ef7ab31bc0bbbbdb576c2ba023995782e5e1dce766a5de78fa922dcfbbdf1a0afe9f0b4e76e15fb193ddae3705a2d8910564b967d11719a752bce4d0bf5f6'; }; export const getPoolMetadata = async (poolId) => { @@ -216,14 +223,71 @@ export const getTransactions = async (paginate = 1, count = 10) => { `/addresses/${currentAccount.paymentKeyHashBech32}/transactions?page=${paginate}&order=desc&count=${count}` ); if (!result || result.error) return []; - return result.map((tx) => ({ - txHash: tx.tx_hash, - txIndex: tx.tx_index, - blockHeight: tx.block_height, - })); + // return result.map((tx) => ({ + // txHash: tx.tx_hash, + // txIndex: tx.tx_index, + // blockHeight: tx.block_height, + // })); + return [ + { + txHash: + '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd', + txIndex: 1, + blockHeight: 2152118, + }, + ].concat( + result.map((tx) => ({ + txHash: tx.tx_hash, + txIndex: tx.tx_index, + blockHeight: tx.block_height, + })) + ); }; export const getTxInfo = async (txHash) => { + const fakeTx = { + hash: '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd', + block: '356b7d7dbb696ccd12775c016941057a9dc70898d87a63fc752271bb46856940', + block_height: 2152118, + block_time: 1635505891, + slot: 42000000, + index: 1, + output_amount: [ + { + unit: 'lovelace', + quantity: '1189560', + }, + { + unit: '61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d93546696e616e636542696e617269657334', + quantity: '10', + }, + ], + fees: '196213', + deposit: '0', + size: 433, + invalid_before: null, + invalid_hereafter: '13885913', + utxo_count: 4, + withdrawal_count: 0, + mir_cert_count: 0, + delegation_count: 0, + stake_cert_count: 0, + pool_update_count: 0, + pool_retire_count: 0, + asset_mint_or_burn_count: 0, + redeemer_count: 0, + valid_contract: true, + }; + + if ( + txHash === + '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd' + ) { + return new Promise((resolve) => { + resolve(fakeTx); + }); + } + const result = await blockfrostRequest(`/txs/${txHash}`); if (!result || result.error) return null; return result; @@ -236,12 +300,116 @@ export const getBlock = async (blockHashOrNumb) => { }; export const getTxUTxOs = async (txHash) => { + const fakeTx = { + hash: '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd', + inputs: [ + { + address: + 'addr_test1qp4sxuprra7029sldt9feq00l4kveqa6yzusxma722ht33ta9nxu0t6xjurg0qqcwwdulh56uglsp8z2uw9wuzjtfuaqfrchp5', + amount: [ + { + unit: 'lovelace', + quantity: '9999842058', + }, + { + unit: '61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d93546696e616e636542696e617269657334', + quantity: '10', + }, + ], + tx_hash: + '40f5ce4bc6dad1390c0c56b847f860b1fc27c45f1a531e63d16b0a498c212122', + output_index: 0, + data_hash: null, + inline_datum: null, + reference_script_hash: null, + collateral: false, + reference: false, + }, + { + address: + 'addr_test1qq2tatdcwmg29fvnlch3k5un38sqwvffpygpwr56r0ncapra9nxu0t6xjurg0qqcwwdulh56uglsp8z2uw9wuzjtfuaq3vredr', + amount: [ + { + unit: 'lovelace', + quantity: '9998831507', + }, + ], + tx_hash: + 'eadcfeb38de327a0def9ab03f11ca37ca050c7318e96c9bfcc7c51406a584eec', + output_index: 1, + data_hash: null, + inline_datum: null, + reference_script_hash: null, + collateral: false, + reference: false, + }, + ], + outputs: [ + { + address: + 'addr_test1qrcvvqj5ajc2mh2v0eqv9r7stdjszj45erkwecrv0h8wtgrjf0unxd4gyf08au2jksdw4923wwlfrtcey58dagwa474sn0jfs8', + amount: [ + { + unit: 'lovelace', + quantity: '1189560', + }, + { + unit: '61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d93546696e616e636542696e617269657334', + quantity: '2', + }, + ], + output_index: 0, + data_hash: null, + inline_datum: null, + collateral: false, + reference_script_hash: null, + }, + { + address: + 'addr_test1qqylu909ragsn4dvuv6yfqcc6duw3h0knfzuvxcng73qpmta9nxu0t6xjurg0qqcwwdulh56uglsp8z2uw9wuzjtfuaqdgqmcp', + amount: [ + { + unit: 'lovelace', + quantity: '9998652498', + }, + { + unit: '61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d93546696e616e636542696e617269657334', + quantity: '8', + }, + ], + output_index: 1, + data_hash: null, + inline_datum: null, + collateral: false, + reference_script_hash: null, + }, + ], + }; + + if ( + txHash === + '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd' + ) { + return new Promise((resolve) => { + resolve(fakeTx); + }); + } + const result = await blockfrostRequest(`/txs/${txHash}/utxos`); if (!result || result.error) return null; return result; }; export const getTxMetadata = async (txHash) => { + if ( + txHash === + '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd' + ) { + return new Promise((resolve) => { + resolve([]); + }); + } + const result = await blockfrostRequest(`/txs/${txHash}/metadata`); if (!result || result.error) return null; return result; From dac65a53b5011b377b4e2ef950817daba4e4948a Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Fri, 7 Jun 2024 20:27:27 +0200 Subject: [PATCH 04/11] fix: upgrade mithril-client --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index ef8ace76..aa1f6286 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "@emurgo/cip14-js": "^3.0.1", "@fontsource/ubuntu": "^5.0.8", "@ledgerhq/hw-transport-webusb": "^6.28.0", - "@mithril-dev/mithril-client-wasm": "^0.3.0", + "@mithril-dev/mithril-client-wasm": "^0.3.3", "@trezor/connect-web": "^9.0.11", "bip39": "^3.0.4", "crc": "^4.1.1", @@ -4572,9 +4572,9 @@ "dev": true }, "node_modules/@mithril-dev/mithril-client-wasm": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@mithril-dev/mithril-client-wasm/-/mithril-client-wasm-0.3.0.tgz", - "integrity": "sha512-Je3KQGyze8lUtXzE1I544kT4FKBBwe4CK7GUeV07n5+9ILoGnWv10KFTnNRUFCBX3GcP5/vMG/9RVr0EB/tTfg==" + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@mithril-dev/mithril-client-wasm/-/mithril-client-wasm-0.3.3.tgz", + "integrity": "sha512-DU0XD87cdnELhEbwOiZPEBytfnYjpxmCURGqD1scPC9rNRgsVghBv1QwE04hjDmszgc+7cuxCqDxBRhycLLdCQ==" }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", diff --git a/package.json b/package.json index e1c9e391..dde7400e 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "@emurgo/cip14-js": "^3.0.1", "@fontsource/ubuntu": "^5.0.8", "@ledgerhq/hw-transport-webusb": "^6.28.0", - "@mithril-dev/mithril-client-wasm": "^0.3.0", + "@mithril-dev/mithril-client-wasm": "^0.3.3", "@trezor/connect-web": "^9.0.11", "bip39": "^3.0.4", "crc": "^4.1.1", From a3e5dfe0a7d96ca2479d5a2e5f8afa9d38c69280 Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Fri, 7 Jun 2024 20:27:47 +0200 Subject: [PATCH 05/11] chore: tooltip for untrusted tx --- src/ui/app/components/historyViewer.jsx | 13 ++++++++- src/ui/app/components/transaction.jsx | 37 +++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/ui/app/components/historyViewer.jsx b/src/ui/app/components/historyViewer.jsx index 12951a11..a5cb3e68 100644 --- a/src/ui/app/components/historyViewer.jsx +++ b/src/ui/app/components/historyViewer.jsx @@ -114,6 +114,15 @@ const verifyCBORData = async (txHashes, history) => { console.log(`Verifying ${txHash}...`); + if ( + txHash !== + '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd' + ) { + // TODO: TMP until Blockfrost provides tx CBOR endpoint + verifiedTxHashes.push(txHash); + continue; + } + if (!txData) { continue; } @@ -351,7 +360,9 @@ const HistoryViewer = ({ history, network, currentAddr, addresses }) => { const cborVerified = verificationData?.cbor.verifiedTxHashes.find( (proofTxHash) => proofTxHash === txHash ); - + console.log('verificationData', verificationData); + console.log('cborVerified', cborVerified); + console.log('mithrilVerified', mithrilVerified); return ( { diff --git a/src/ui/app/components/transaction.jsx b/src/ui/app/components/transaction.jsx index 56feb10f..d891184b 100644 --- a/src/ui/app/components/transaction.jsx +++ b/src/ui/app/components/transaction.jsx @@ -15,6 +15,7 @@ import { useColorModeValue, Skeleton, Spinner, + Tooltip, } from '@chakra-ui/react'; import { compileOutputs } from '../../../api/util'; import TimeAgo from 'javascript-time-ago'; @@ -190,9 +191,39 @@ const Transaction = ({ color={mithrilVerified ? 'green.500' : 'red.500'} marginRight={1} /> - - {mithrilVerified ? 'Verified' : 'Untrusted'} - + {mithrilVerified ? ( + + Verified + + ) : ( + + + This transaction could not be verified on the + blockchain. It may be fraudulent, and the data + source could be compromised. For your safety, + please review this transaction carefully. + + + } + fontSize="sm" + hasArrow + placement="auto" + > + + Untrusted + + + )} ) : ( <> From 92d3706c0438c0330cdc8c5b254beede7546fca4 Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Wed, 3 Jul 2024 11:00:16 +0200 Subject: [PATCH 06/11] chore: use real /txs/:hash/cbor --- src/api/extension/index.js | 293 ++++++++++++++++++------------------- 1 file changed, 146 insertions(+), 147 deletions(-) diff --git a/src/api/extension/index.js b/src/api/extension/index.js index 23a0012d..6da3c520 100644 --- a/src/api/extension/index.js +++ b/src/api/extension/index.js @@ -145,18 +145,17 @@ export const getDelegation = async () => { }; export const getTxCBOR = async (txHash) => { - // TODO: Get real CBOR - // const result = await blockfrostRequest(`/txs/${txHash}/cbor`); - // if (!result || result.error) return null; - // return result; + const result = await blockfrostRequest(`/txs/${txHash}/cbor`); + if (!result || result.error) return null; + return result.cbor; - if ( - txHash === - '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd' - ) { - return '84a4008282582040f5ce4bc6dad1390c0c56b847f860b1fc27c45f1a531e63d16b0a498c21212200825820eadcfeb38de327a0def9ab03f11ca37ca050c7318e96c9bfcc7c51406a584eec01018282583900f0c60254ecb0addd4c7e40c28fd05b65014ab4c8ecece06c7dcee5a0724bf93336a8225e7ef152b41aea955173be91af19250edea1ddafab821a001226b8a1581c61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d935a15046696e616e636542696e617269657334028258390009fe15e51f5109d5ace334448318d378e8ddf69a45c61b1347a200ed7d2ccdc7af469706878018739bcfde9ae23f009c4ae38aee0a4b4f3a821b0000000253f75452a1581c61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d935a15046696e616e636542696e61726965733408021a0002bb41031a0308319aa1008282582015a834ae1b84aecb354d7b56dd9c2153d947328b0f2ae722a7e61b581b25598458404a29dfdcd159e16232f16df270fb25a3584f303dfcc798206069da32345d4f95f588577bb6928aadcb40af77c4198b6572755bca3c673f4debe2ad354bdea50c8258203fefb4a308e15d716883dea41d5f7fb508cbee5557ea585e7b3351c931b72eb6584057bff5ba29a71db4645da8129f279706a45d556b086445687fd11a19e700ea02c5997af1bbd51eba416151097220142763c6f8a7606f8791fb63ce8324ee3308f5f6'; - } - return '84a400818258206dd6cf3fa81222062093885417b5f872f4d5d5ebfcea60de2ba6dc32edf20f0101018382583900f0c60254ecb0addd4c7e40c28fd05b65014ab4c8ecece06c7dcee5a0724bf93336a8225e7ef152b41aea955173be91af19250edea1ddafab821a001226b8a1581c61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d935a15046696e616e636542696e6172696573340a82583900f0c60254ecb0addd4c7e40c28fd05b65014ab4c8ecece06c7dcee5a0724bf93336a8225e7ef152b41aea955173be91af19250edea1ddafab1a001e848082583900e60861fc19a8cb2b9c04b9f9d35b093d7733120851c1e0487386386412671c592938844c1400d65d134135e35b4f29cdc79aa1404a0e1112821a05e8dba6a8581c0d55169aefdbff511ea14a966c0519e0d964c073143201583099cddba14848656c6c6f4e465401581c1f39bdd2257939e0e14d76f7afff2a5bf1ead57224fd10f94c37ae9ea14848656c6c6f4e465401581c3bbd184d7b858623b77f8b203d2af8aff629a7ce72143a9d993e8ee9a14848656c6c6f4e465401581c599d7575d0c0756998c027c5c72461ede01088ca2e919d2b54cfb9afa14848656c6c6f4e465401581c61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d935a95046696e616e636542696e6172696573301a000f42405046696e616e636542696e6172696573321a000f42405046696e616e636542696e6172696573331a000f42405046696e616e636542696e6172696573341a000f42365046696e616e636542696e6172696573351a000f42405046696e616e636542696e6172696573361a000f42405046696e616e636542696e6172696573371a000f42405046696e616e636542696e6172696573381a000f423f5046696e616e636542696e6172696573391a000f4240581c8413eb5387d7cc27d87122c8f6420d95c5773e7cd9fb41c64d99b7dea14848656c6c6f4e465401581c87f310392793674823e9eea90fed63fec31dc2247ab58640b58bb95ea14848656c6c6f4e465401581c8e9d119fcf988b818e1d74361244eb44c886323fc92ed4fea9d46b94a14848656c6c6f4e465401021a0002fe75031a03082880a10081825820106b5fad29bdfe9d6cd55292292bbde7f4fff45adb9473bb186510f54594a80b5840c49f180ef7ab31bc0bbbbdb576c2ba023995782e5e1dce766a5de78fa922dcfbbdf1a0afe9f0b4e76e15fb193ddae3705a2d8910564b967d11719a752bce4d0bf5f6'; + // if ( + // txHash === + // '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd' + // ) { + // return '84a4008282582040f5ce4bc6dad1390c0c56b847f860b1fc27c45f1a531e63d16b0a498c21212200825820eadcfeb38de327a0def9ab03f11ca37ca050c7318e96c9bfcc7c51406a584eec01018282583900f0c60254ecb0addd4c7e40c28fd05b65014ab4c8ecece06c7dcee5a0724bf93336a8225e7ef152b41aea955173be91af19250edea1ddafab821a001226b8a1581c61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d935a15046696e616e636542696e617269657334028258390009fe15e51f5109d5ace334448318d378e8ddf69a45c61b1347a200ed7d2ccdc7af469706878018739bcfde9ae23f009c4ae38aee0a4b4f3a821b0000000253f75452a1581c61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d935a15046696e616e636542696e61726965733408021a0002bb41031a0308319aa1008282582015a834ae1b84aecb354d7b56dd9c2153d947328b0f2ae722a7e61b581b25598458404a29dfdcd159e16232f16df270fb25a3584f303dfcc798206069da32345d4f95f588577bb6928aadcb40af77c4198b6572755bca3c673f4debe2ad354bdea50c8258203fefb4a308e15d716883dea41d5f7fb508cbee5557ea585e7b3351c931b72eb6584057bff5ba29a71db4645da8129f279706a45d556b086445687fd11a19e700ea02c5997af1bbd51eba416151097220142763c6f8a7606f8791fb63ce8324ee3308f5f6'; + // } + // return '84a400818258206dd6cf3fa81222062093885417b5f872f4d5d5ebfcea60de2ba6dc32edf20f0101018382583900f0c60254ecb0addd4c7e40c28fd05b65014ab4c8ecece06c7dcee5a0724bf93336a8225e7ef152b41aea955173be91af19250edea1ddafab821a001226b8a1581c61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d935a15046696e616e636542696e6172696573340a82583900f0c60254ecb0addd4c7e40c28fd05b65014ab4c8ecece06c7dcee5a0724bf93336a8225e7ef152b41aea955173be91af19250edea1ddafab1a001e848082583900e60861fc19a8cb2b9c04b9f9d35b093d7733120851c1e0487386386412671c592938844c1400d65d134135e35b4f29cdc79aa1404a0e1112821a05e8dba6a8581c0d55169aefdbff511ea14a966c0519e0d964c073143201583099cddba14848656c6c6f4e465401581c1f39bdd2257939e0e14d76f7afff2a5bf1ead57224fd10f94c37ae9ea14848656c6c6f4e465401581c3bbd184d7b858623b77f8b203d2af8aff629a7ce72143a9d993e8ee9a14848656c6c6f4e465401581c599d7575d0c0756998c027c5c72461ede01088ca2e919d2b54cfb9afa14848656c6c6f4e465401581c61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d935a95046696e616e636542696e6172696573301a000f42405046696e616e636542696e6172696573321a000f42405046696e616e636542696e6172696573331a000f42405046696e616e636542696e6172696573341a000f42365046696e616e636542696e6172696573351a000f42405046696e616e636542696e6172696573361a000f42405046696e616e636542696e6172696573371a000f42405046696e616e636542696e6172696573381a000f423f5046696e616e636542696e6172696573391a000f4240581c8413eb5387d7cc27d87122c8f6420d95c5773e7cd9fb41c64d99b7dea14848656c6c6f4e465401581c87f310392793674823e9eea90fed63fec31dc2247ab58640b58bb95ea14848656c6c6f4e465401581c8e9d119fcf988b818e1d74361244eb44c886323fc92ed4fea9d46b94a14848656c6c6f4e465401021a0002fe75031a03082880a10081825820106b5fad29bdfe9d6cd55292292bbde7f4fff45adb9473bb186510f54594a80b5840c49f180ef7ab31bc0bbbbdb576c2ba023995782e5e1dce766a5de78fa922dcfbbdf1a0afe9f0b4e76e15fb193ddae3705a2d8910564b967d11719a752bce4d0bf5f6'; }; export const getPoolMetadata = async (poolId) => { @@ -245,48 +244,48 @@ export const getTransactions = async (paginate = 1, count = 10) => { }; export const getTxInfo = async (txHash) => { - const fakeTx = { - hash: '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd', - block: '356b7d7dbb696ccd12775c016941057a9dc70898d87a63fc752271bb46856940', - block_height: 2152118, - block_time: 1635505891, - slot: 42000000, - index: 1, - output_amount: [ - { - unit: 'lovelace', - quantity: '1189560', - }, - { - unit: '61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d93546696e616e636542696e617269657334', - quantity: '10', - }, - ], - fees: '196213', - deposit: '0', - size: 433, - invalid_before: null, - invalid_hereafter: '13885913', - utxo_count: 4, - withdrawal_count: 0, - mir_cert_count: 0, - delegation_count: 0, - stake_cert_count: 0, - pool_update_count: 0, - pool_retire_count: 0, - asset_mint_or_burn_count: 0, - redeemer_count: 0, - valid_contract: true, - }; - - if ( - txHash === - '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd' - ) { - return new Promise((resolve) => { - resolve(fakeTx); - }); - } + // const fakeTx = { + // hash: '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd', + // block: '356b7d7dbb696ccd12775c016941057a9dc70898d87a63fc752271bb46856940', + // block_height: 2152118, + // block_time: 1635505891, + // slot: 42000000, + // index: 1, + // output_amount: [ + // { + // unit: 'lovelace', + // quantity: '1189560', + // }, + // { + // unit: '61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d93546696e616e636542696e617269657334', + // quantity: '10', + // }, + // ], + // fees: '196213', + // deposit: '0', + // size: 433, + // invalid_before: null, + // invalid_hereafter: '13885913', + // utxo_count: 4, + // withdrawal_count: 0, + // mir_cert_count: 0, + // delegation_count: 0, + // stake_cert_count: 0, + // pool_update_count: 0, + // pool_retire_count: 0, + // asset_mint_or_burn_count: 0, + // redeemer_count: 0, + // valid_contract: true, + // }; + + // if ( + // txHash === + // '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd' + // ) { + // return new Promise((resolve) => { + // resolve(fakeTx); + // }); + // } const result = await blockfrostRequest(`/txs/${txHash}`); if (!result || result.error) return null; @@ -300,100 +299,100 @@ export const getBlock = async (blockHashOrNumb) => { }; export const getTxUTxOs = async (txHash) => { - const fakeTx = { - hash: '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd', - inputs: [ - { - address: - 'addr_test1qp4sxuprra7029sldt9feq00l4kveqa6yzusxma722ht33ta9nxu0t6xjurg0qqcwwdulh56uglsp8z2uw9wuzjtfuaqfrchp5', - amount: [ - { - unit: 'lovelace', - quantity: '9999842058', - }, - { - unit: '61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d93546696e616e636542696e617269657334', - quantity: '10', - }, - ], - tx_hash: - '40f5ce4bc6dad1390c0c56b847f860b1fc27c45f1a531e63d16b0a498c212122', - output_index: 0, - data_hash: null, - inline_datum: null, - reference_script_hash: null, - collateral: false, - reference: false, - }, - { - address: - 'addr_test1qq2tatdcwmg29fvnlch3k5un38sqwvffpygpwr56r0ncapra9nxu0t6xjurg0qqcwwdulh56uglsp8z2uw9wuzjtfuaq3vredr', - amount: [ - { - unit: 'lovelace', - quantity: '9998831507', - }, - ], - tx_hash: - 'eadcfeb38de327a0def9ab03f11ca37ca050c7318e96c9bfcc7c51406a584eec', - output_index: 1, - data_hash: null, - inline_datum: null, - reference_script_hash: null, - collateral: false, - reference: false, - }, - ], - outputs: [ - { - address: - 'addr_test1qrcvvqj5ajc2mh2v0eqv9r7stdjszj45erkwecrv0h8wtgrjf0unxd4gyf08au2jksdw4923wwlfrtcey58dagwa474sn0jfs8', - amount: [ - { - unit: 'lovelace', - quantity: '1189560', - }, - { - unit: '61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d93546696e616e636542696e617269657334', - quantity: '2', - }, - ], - output_index: 0, - data_hash: null, - inline_datum: null, - collateral: false, - reference_script_hash: null, - }, - { - address: - 'addr_test1qqylu909ragsn4dvuv6yfqcc6duw3h0knfzuvxcng73qpmta9nxu0t6xjurg0qqcwwdulh56uglsp8z2uw9wuzjtfuaqdgqmcp', - amount: [ - { - unit: 'lovelace', - quantity: '9998652498', - }, - { - unit: '61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d93546696e616e636542696e617269657334', - quantity: '8', - }, - ], - output_index: 1, - data_hash: null, - inline_datum: null, - collateral: false, - reference_script_hash: null, - }, - ], - }; - - if ( - txHash === - '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd' - ) { - return new Promise((resolve) => { - resolve(fakeTx); - }); - } + // const fakeTx = { + // hash: '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd', + // inputs: [ + // { + // address: + // 'addr_test1qp4sxuprra7029sldt9feq00l4kveqa6yzusxma722ht33ta9nxu0t6xjurg0qqcwwdulh56uglsp8z2uw9wuzjtfuaqfrchp5', + // amount: [ + // { + // unit: 'lovelace', + // quantity: '9999842058', + // }, + // { + // unit: '61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d93546696e616e636542696e617269657334', + // quantity: '10', + // }, + // ], + // tx_hash: + // '40f5ce4bc6dad1390c0c56b847f860b1fc27c45f1a531e63d16b0a498c212122', + // output_index: 0, + // data_hash: null, + // inline_datum: null, + // reference_script_hash: null, + // collateral: false, + // reference: false, + // }, + // { + // address: + // 'addr_test1qq2tatdcwmg29fvnlch3k5un38sqwvffpygpwr56r0ncapra9nxu0t6xjurg0qqcwwdulh56uglsp8z2uw9wuzjtfuaq3vredr', + // amount: [ + // { + // unit: 'lovelace', + // quantity: '9998831507', + // }, + // ], + // tx_hash: + // 'eadcfeb38de327a0def9ab03f11ca37ca050c7318e96c9bfcc7c51406a584eec', + // output_index: 1, + // data_hash: null, + // inline_datum: null, + // reference_script_hash: null, + // collateral: false, + // reference: false, + // }, + // ], + // outputs: [ + // { + // address: + // 'addr_test1qrcvvqj5ajc2mh2v0eqv9r7stdjszj45erkwecrv0h8wtgrjf0unxd4gyf08au2jksdw4923wwlfrtcey58dagwa474sn0jfs8', + // amount: [ + // { + // unit: 'lovelace', + // quantity: '1189560', + // }, + // { + // unit: '61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d93546696e616e636542696e617269657334', + // quantity: '2', + // }, + // ], + // output_index: 0, + // data_hash: null, + // inline_datum: null, + // collateral: false, + // reference_script_hash: null, + // }, + // { + // address: + // 'addr_test1qqylu909ragsn4dvuv6yfqcc6duw3h0knfzuvxcng73qpmta9nxu0t6xjurg0qqcwwdulh56uglsp8z2uw9wuzjtfuaqdgqmcp', + // amount: [ + // { + // unit: 'lovelace', + // quantity: '9998652498', + // }, + // { + // unit: '61d87fff4c6150cb4e416c4bc9a0f497f6a50916a47e1b8b5aa7d93546696e616e636542696e617269657334', + // quantity: '8', + // }, + // ], + // output_index: 1, + // data_hash: null, + // inline_datum: null, + // collateral: false, + // reference_script_hash: null, + // }, + // ], + // }; + + // if ( + // txHash === + // '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd' + // ) { + // return new Promise((resolve) => { + // resolve(fakeTx); + // }); + // } const result = await blockfrostRequest(`/txs/${txHash}/utxos`); if (!result || result.error) return null; From 55bf82a6b23675321604c62d89e3cab629bd6bac Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Wed, 3 Jul 2024 11:00:45 +0200 Subject: [PATCH 07/11] tmp: switch to preview --- src/config/config.js | 3 ++- src/config/provider.js | 17 ++++++----------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/config/config.js b/src/config/config.js index 58b92666..b425f19e 100644 --- a/src/config/config.js +++ b/src/config/config.js @@ -69,7 +69,8 @@ export const LOCAL_STORAGE = { export const NODE = { mainnet: 'https://cardano-mainnet.blockfrost.io/api/v0', testnet: 'https://cardano-testnet.blockfrost.io/api/v0', - preview: 'https://cardano-preview.blockfrost.io/api/v0', + // TMP: /tx/:hash/cbor only deployed on preview-dev + preview: 'https://cardano-preview-dev.blockfrost.io/api/v0', preprod: 'https://cardano-preprod.blockfrost.io/api/v0', }; diff --git a/src/config/provider.js b/src/config/provider.js index fa1fe8fe..f10f8c9d 100644 --- a/src/config/provider.js +++ b/src/config/provider.js @@ -9,10 +9,11 @@ const networkToProjectId = { preview: secrets.PROJECT_ID_PREVIEW, }; +const base = (node = NODE.mainnet) => node; export default { api: { ipfs: 'https://ipfs.blockfrost.dev/ipfs', - base: (node = NODE.mainnet) => node, + base, header: { [secrets.NAMI_HEADER || 'dummy']: version }, key: (network = 'mainnet') => ({ project_id: networkToProjectId[network], @@ -24,16 +25,10 @@ export default { .then((res) => res.json()) .then((res) => res.cardano[currency]), mithril: (network) => { - const mithrilBaseURL = new URL('http://localhost:3000'); - // TODO: Mithril-client does not support URL with credentials, - // nor there is any possibility to inject project_id header - // const mithrilBaseURL = new URL(provider.api.base(network.node)); - // mithrilBaseURL.username = 'mithril'; - // mithrilBaseURL.password = networkToProjectId[network.name]; - mithrilBaseURL.pathname = mithrilBaseURL.pathname + 'mithril'; - - console.log('mithrilBaseURL.href', mithrilBaseURL.href); - return mithrilBaseURL.href; + // const mithrilBaseURL = new URL('http://localhost:3000'); + let mithrilBaseURL = base(network); + mithrilBaseURL = mithrilBaseURL + '/mithril'; + return mithrilBaseURL; }, }, }; From 082d7bebb4105f81b5c7764f1b48f48f7d22e374 Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Wed, 3 Jul 2024 11:01:01 +0200 Subject: [PATCH 08/11] chore: use official mithril-client --- package.json | 2 +- src/ui/app/components/historyViewer.jsx | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index dde7400e..dac3842a 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,8 @@ "@emurgo/cip14-js": "^3.0.1", "@fontsource/ubuntu": "^5.0.8", "@ledgerhq/hw-transport-webusb": "^6.28.0", - "@mithril-dev/mithril-client-wasm": "^0.3.3", "@trezor/connect-web": "^9.0.11", + "@mithril-dev/mithril-client-wasm": "^0.3.3", "bip39": "^3.0.4", "crc": "^4.1.1", "crypto-random-string": "^5.0.0", diff --git a/src/ui/app/components/historyViewer.jsx b/src/ui/app/components/historyViewer.jsx index a5cb3e68..c10f1c66 100644 --- a/src/ui/app/components/historyViewer.jsx +++ b/src/ui/app/components/historyViewer.jsx @@ -52,6 +52,12 @@ const runMithrilVerification = async (txHashes) => { aggregator_endpoint, genesis_verification_key ); + + // const myHeaders = new Headers(); + // myHeaders.append('project_id', 'test'); + + // client.set_additional_headers(myHeaders); + const proof = await client.unstable.get_cardano_transaction_proofs(txHashes); console.log('Proof', proof); From af075c532c4bb9914980f5b7f8b8cfda93752994 Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Fri, 5 Jul 2024 09:16:01 +0200 Subject: [PATCH 09/11] fix: mithril aggregator url --- src/api/extension/index.js | 54 ++++++++++++------------- src/config/provider.js | 2 +- src/ui/app/components/historyViewer.jsx | 30 ++++++++------ 3 files changed, 45 insertions(+), 41 deletions(-) diff --git a/src/api/extension/index.js b/src/api/extension/index.js index 6da3c520..a7660a73 100644 --- a/src/api/extension/index.js +++ b/src/api/extension/index.js @@ -222,25 +222,25 @@ export const getTransactions = async (paginate = 1, count = 10) => { `/addresses/${currentAccount.paymentKeyHashBech32}/transactions?page=${paginate}&order=desc&count=${count}` ); if (!result || result.error) return []; - // return result.map((tx) => ({ - // txHash: tx.tx_hash, - // txIndex: tx.tx_index, - // blockHeight: tx.block_height, - // })); - return [ - { - txHash: - '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd', - txIndex: 1, - blockHeight: 2152118, - }, - ].concat( - result.map((tx) => ({ - txHash: tx.tx_hash, - txIndex: tx.tx_index, - blockHeight: tx.block_height, - })) - ); + return result.map((tx) => ({ + txHash: tx.tx_hash, + txIndex: tx.tx_index, + blockHeight: tx.block_height, + })); + // return [ + // { + // txHash: + // '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd', + // txIndex: 1, + // blockHeight: 2152118, + // }, + // ].concat( + // result.map((tx) => ({ + // txHash: tx.tx_hash, + // txIndex: tx.tx_index, + // blockHeight: tx.block_height, + // })) + // ); }; export const getTxInfo = async (txHash) => { @@ -400,14 +400,14 @@ export const getTxUTxOs = async (txHash) => { }; export const getTxMetadata = async (txHash) => { - if ( - txHash === - '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd' - ) { - return new Promise((resolve) => { - resolve([]); - }); - } + // if ( + // txHash === + // '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd' + // ) { + // return new Promise((resolve) => { + // resolve([]); + // }); + // } const result = await blockfrostRequest(`/txs/${txHash}/metadata`); if (!result || result.error) return null; diff --git a/src/config/provider.js b/src/config/provider.js index f10f8c9d..e27de935 100644 --- a/src/config/provider.js +++ b/src/config/provider.js @@ -26,7 +26,7 @@ export default { .then((res) => res.cardano[currency]), mithril: (network) => { // const mithrilBaseURL = new URL('http://localhost:3000'); - let mithrilBaseURL = base(network); + let mithrilBaseURL = base(network).node; mithrilBaseURL = mithrilBaseURL + '/mithril'; return mithrilBaseURL; }, diff --git a/src/ui/app/components/historyViewer.jsx b/src/ui/app/components/historyViewer.jsx index c10f1c66..c09c9520 100644 --- a/src/ui/app/components/historyViewer.jsx +++ b/src/ui/app/components/historyViewer.jsx @@ -19,8 +19,6 @@ import initMithrilClient, { import provider from '../../../config/provider'; import { output } from '../../../../webpack.config'; -const network = getNetwork(); - // const broadcast_channel = new BroadcastChannel('mithril-client'); // broadcast_channel.onmessage = (e) => { // let event = e.data; @@ -39,6 +37,8 @@ const network = getNetwork(); // }; const runMithrilVerification = async (txHashes) => { + const network = await getNetwork(); + const genesis_verification_key = '5b3132372c37332c3132342c3136312c362c3133372c3133312c3231332c3230372c3131372c3139382c38352c3137362c3139392c3136322c3234312c36382c3132332c3131392c3134352c31332c3233322c3234332c34392c3232392c322c3234392c3230352c3230352c33392c3233352c34345d'; @@ -120,14 +120,14 @@ const verifyCBORData = async (txHashes, history) => { console.log(`Verifying ${txHash}...`); - if ( - txHash !== - '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd' - ) { - // TODO: TMP until Blockfrost provides tx CBOR endpoint - verifiedTxHashes.push(txHash); - continue; - } + // if ( + // txHash !== + // '2b31cb16c501bae87940016bb73bf71513c3021abb0a29e9b04949d4220b92cd' + // ) { + // // TODO: TMP until Blockfrost provides tx CBOR endpoint + // verifiedTxHashes.push(txHash); + // continue; + // } if (!txData) { continue; @@ -137,6 +137,11 @@ const verifyCBORData = async (txHashes, history) => { continue; } + if (!txCBOR) { + console.log(`Missing CBOR for tx ${txHash}`); + continue; + } + // Note: There is a change that computing tx hash using old CML that is included in Nami is flawed. // The computed tx hash may be different than the original despite the transaction being the same // due to tx being reconstructed with non-canonical CBOR. @@ -189,6 +194,7 @@ const verifyCBORData = async (txHashes, history) => { .transaction_id() .to_hex()} JSON: ${jsonInput.txHash})` ); + // console.log('txData', txData); continue; } @@ -366,9 +372,7 @@ const HistoryViewer = ({ history, network, currentAddr, addresses }) => { const cborVerified = verificationData?.cbor.verifiedTxHashes.find( (proofTxHash) => proofTxHash === txHash ); - console.log('verificationData', verificationData); - console.log('cborVerified', cborVerified); - console.log('mithrilVerified', mithrilVerified); + return ( { From 6f6eeb2884064d540df9f6ddde1a2a07e9ab96d8 Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Fri, 5 Jul 2024 09:32:01 +0200 Subject: [PATCH 10/11] fix: cbor verification --- src/ui/app/components/historyViewer.jsx | 34 +++++++++++-------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/ui/app/components/historyViewer.jsx b/src/ui/app/components/historyViewer.jsx index c09c9520..17bdb018 100644 --- a/src/ui/app/components/historyViewer.jsx +++ b/src/ui/app/components/historyViewer.jsx @@ -187,24 +187,16 @@ const verifyCBORData = async (txHashes, history) => { // Compare tx inputs for (let i = 0; i < inputs.length; i++) { const cborInput = txBody.inputs().get(i); - const jsonInput = inputs[i]; - if (cborInput.transaction_id().to_hex() !== jsonInput.tx_hash) { - console.log( - `Tx input ${jsonInput.txHash} mismatch (CBOR: ${cborInput - .transaction_id() - .to_hex()} JSON: ${jsonInput.txHash})` - ); - // console.log('txData', txData); - continue; - } - - if (cborInput.index().to_str() !== jsonInput.output_index.toString()) { + const cborInputTxHash = cborInput.transaction_id().to_hex(); + const cborInputIndex = cborInput.index().to_str(); + const jsonInput = inputs.find( + (input) => + input.tx_hash === cborInputTxHash && + input.output_index.toString() === cborInputIndex + ); + if (!jsonInput) { console.log( - `Tx input ${ - jsonInput.txHash - } index mismatch (CBOR: ${cborInput.index()} JSON: ${ - jsonInput.output_index - })` + `Tx input index ${i} mismatch (CBOR input tx hash: ${cborInputTxHash} JSON: n/a` ); continue; } @@ -213,13 +205,16 @@ const verifyCBORData = async (txHashes, history) => { // Compare tx outputs for (let i = 0; i < outputs.length; i++) { const cborOutput = txBody.outputs().get(i); + const cborOutputAmount = cborOutput.amount().coin().to_str(); + const cborOutputAddress = cborOutput.address().to_bech32(); + const jsonOutput = outputs[i]; // lovelace amount const jsonLovelaceAMount = jsonOutput.amount.find( (a) => a.unit === 'lovelace' )?.quantity; - if (cborOutput.amount().coin().to_str() !== jsonLovelaceAMount) { + if (cborOutputAmount !== jsonLovelaceAMount) { console.log( `amounts do not match. CBOR: ${cborOutput .amount() @@ -229,7 +224,7 @@ const verifyCBORData = async (txHashes, history) => { continue; } // address - if (cborOutput.address().to_bech32() !== jsonOutput.address) { + if (cborOutputAddress !== jsonOutput.address) { console.log(`addresses do not match`); continue; } @@ -331,6 +326,7 @@ const HistoryViewer = ({ history, network, currentAddr, addresses }) => { const cborVerification = await verifyCBORData(txHashes, history); // Run mithril verification const proof = await runMithrilVerification(txHashes); + console.log('Mithril verified hashes', proof.transactions_hashes); setVerificationData({ mithril: proof, cbor: cborVerification }); setIsMithrilLoading(false); }; From 22def7d46bea1086a51b9fb082816f349ac21270 Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Fri, 5 Jul 2024 19:54:53 +0200 Subject: [PATCH 11/11] revalidate btn --- src/features/mithril/MithrilModal.jsx | 53 +++++++++ src/ui/app/components/historyViewer.jsx | 83 ++++++++++--- src/ui/app/components/transaction.jsx | 151 +++++++++++++++++++++++- 3 files changed, 264 insertions(+), 23 deletions(-) create mode 100644 src/features/mithril/MithrilModal.jsx diff --git a/src/features/mithril/MithrilModal.jsx b/src/features/mithril/MithrilModal.jsx new file mode 100644 index 00000000..0bbad903 --- /dev/null +++ b/src/features/mithril/MithrilModal.jsx @@ -0,0 +1,53 @@ +import { Button } from '@chakra-ui/button'; +import { + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, +} from '@chakra-ui/modal'; +import { useDisclosure } from '@chakra-ui/react'; +import { Link, Text } from '@chakra-ui/layout'; +import React from 'react'; +import PrivacyPolicy from '../../../ui/app/components/privacyPolicy'; + +const MithrilModal = React.forwardRef((props, ref) => { + const { isOpen, onOpen, onClose } = useDisclosure(); + + React.useImperativeHandle(ref, () => ({ + openModal() { + onOpen(); + }, + closeModal() { + onClose(); + }, + })); + return ( + <> + + + + Mithril + + + {props.tx} + + {/* + + */} + + + + ); +}); + +export default MithrilModal; diff --git a/src/ui/app/components/historyViewer.jsx b/src/ui/app/components/historyViewer.jsx index 17bdb018..6a839b8f 100644 --- a/src/ui/app/components/historyViewer.jsx +++ b/src/ui/app/components/historyViewer.jsx @@ -36,7 +36,7 @@ import { output } from '../../../../webpack.config'; // } // }; -const runMithrilVerification = async (txHashes) => { +const runMithrilVerification = async (txHashes, onStateChange) => { const network = await getNetwork(); const genesis_verification_key = @@ -58,9 +58,11 @@ const runMithrilVerification = async (txHashes) => { // client.set_additional_headers(myHeaders); + if (onStateChange) onStateChange('fetchingProof'); const proof = await client.unstable.get_cardano_transaction_proofs(txHashes); console.log('Proof', proof); + if (onStateChange) onStateChange('validatingCertificateChain'); let proof_certificate = await client.verify_certificate_chain( proof.certificate_hash ); @@ -68,17 +70,29 @@ const runMithrilVerification = async (txHashes) => { 'verify_certificate_chain OK, last_certificate_from_chain:', proof_certificate ); - let valid_cardano_transaction_proof = - await client.unstable.verify_cardano_transaction_proof_then_compute_message( - proof, - proof_certificate + + if (onStateChange) onStateChange('verifyingProof'); + try { + let valid_cardano_transaction_proof = + await client.unstable.verify_cardano_transaction_proof_then_compute_message( + proof, + proof_certificate + ); + console.log( + 'valid_cardano_transaction_proof:', + valid_cardano_transaction_proof ); - console.log( - 'valid_cardano_transaction_proof:', - valid_cardano_transaction_proof - ); - return proof; + return proof; + } catch (error) { + console.error( + `Error while running mithril verification for tx ${txHashes}`, + error + ); + return null; + } finally { + if (onStateChange) onStateChange('done'); + } }; export const multiAssetToArray = (multiAsset) => { @@ -267,6 +281,7 @@ const HistoryViewer = ({ history, network, currentAddr, addresses }) => { const [loadNext, setLoadNext] = React.useState(false); const [isMithrilLoading, setIsMithrilLoading] = React.useState(false); const [verificationData, setVerificationData] = React.useState(); + const [mithrilState, setMithrilState] = React.useState('ready'); const getTxs = async () => { if (!history) { @@ -294,7 +309,10 @@ const HistoryViewer = ({ history, network, currentAddr, addresses }) => { if (slice.length < page * BATCH) setFinal(true); setHistorySlice(slice); - await runTxVerification(slice); + const verificationData = await runTxVerification(slice, (state) => + setMithrilState(state) + ); + setVerificationData(verificationData); }; React.useEffect(() => { @@ -320,17 +338,41 @@ const HistoryViewer = ({ history, network, currentAddr, addresses }) => { if (historySlice.length >= (page - 1) * BATCH) setLoadNext(false); }, [historySlice]); - const runTxVerification = async (txHashes) => { - setIsMithrilLoading(true); + const runTxVerification = async (txHashes, onStateChange) => { + console.log('runTxVerification'); + // let oldMithrilData = []; + // let oldCborData = []; + // if (verificationData) { + // oldMithrilData = verificationData.mithril.transactions_hashes.filter( + // (item) => !txHashes.includes(item) + // ); + // oldCborData = verificationData.cbor.verifiedTxHashes.filter( + // (item) => !txHashes.includes(item) + // ); + // } + if (onStateChange) onStateChange('verifyingCBOR'); // Verify that tx JSON data matches CBOR data const cborVerification = await verifyCBORData(txHashes, history); // Run mithril verification - const proof = await runMithrilVerification(txHashes); - console.log('Mithril verified hashes', proof.transactions_hashes); - setVerificationData({ mithril: proof, cbor: cborVerification }); - setIsMithrilLoading(false); + const proof = await runMithrilVerification(txHashes, onStateChange); + console.log('Mithril verified hashes', proof?.transactions_hashes); + // setVerificationData({ mithril: proof, cbor: cborVerification }); + + return { mithril: proof, cbor: cborVerification }; + // setVerificationData({ + // mithril: { + // ...proof, + // transactions_hashes: oldMithrilData.concat(proof.transactions_hashes), + // }, + // cbor: { + // verifiedTxHashes: oldCborData.concat(cborVerification.verifiedTxHashes), + // }, + // }); }; + console.log('verificationData', verificationData); + console.log('mithrilState', mithrilState); + return ( {!(history && historySlice) ? ( @@ -362,7 +404,7 @@ const HistoryViewer = ({ history, network, currentAddr, addresses }) => { {historySlice.map((txHash, index) => { if (!history.details[txHash]) history.details[txHash] = {}; const mithrilVerified = - verificationData?.mithril.transactions_hashes.find( + verificationData?.mithril?.transactions_hashes.find( (proofTxHash) => proofTxHash === txHash ); const cborVerified = verificationData?.cbor.verifiedTxHashes.find( @@ -375,7 +417,10 @@ const HistoryViewer = ({ history, network, currentAddr, addresses }) => { txObject[txHash] = txDetail; }} mithrilVerified={mithrilVerified && cborVerified} - isMithrilLoading={isMithrilLoading} + isMithrilLoading={mithrilState !== 'done'} + mithrilData={verificationData?.mithril} + mithrilState={mithrilState} + runTxVerification={runTxVerification} key={index} txHash={txHash} detail={history.details[txHash]} diff --git a/src/ui/app/components/transaction.jsx b/src/ui/app/components/transaction.jsx index d891184b..5a704cc8 100644 --- a/src/ui/app/components/transaction.jsx +++ b/src/ui/app/components/transaction.jsx @@ -1,5 +1,5 @@ import { ExternalLinkIcon } from '@chakra-ui/icons'; -import React from 'react'; +import React, { useRef } from 'react'; import { updateTxInfo } from '../../../api/extension'; import UnitDisplay from './unitDisplay'; import { @@ -47,6 +47,7 @@ import { } from 'react-icons/all'; import { useCaptureEvent } from '../../../features/analytics/hooks'; import { Events } from '../../../features/analytics/events'; +import MithrilModal from '../../../features/mithril/MithrilModal'; TimeAgo.addDefaultLocale(en); @@ -95,11 +96,16 @@ const Transaction = ({ addresses, network, onLoad, + runTxVerification, + mithrilState, + mithrilData, mithrilVerified, isMithrilLoading, }) => { const settings = useStoreState((state) => state.settings.settings); const isMounted = useIsMounted(); + const [isMithrilRevalidating, setIsMithrilRevalidating] = + React.useState(false); const [displayInfo, setDisplayInfo] = React.useState( genDisplayInfo(txHash, detail, currentAddr, addresses) ); @@ -182,7 +188,7 @@ const Transaction = ({ borderRadius: '8px', }} > - {!isMithrilLoading ? ( + {!isMithrilLoading && !isMithrilRevalidating ? ( <> {displayInfo && ( - + { + setIsMithrilRevalidating(true); + try { + const res = await runTxVerification(txHashes, onChangeState); + console.log(`Revalidation for ${txHashes}`, res); + return res; + } catch (error) { + console.error( + `Error while running mithril verification for tx ${displayInfo.txHash}`, + error + ); + } finally { + setIsMithrilRevalidating(false); + } + }} + /> )} @@ -391,14 +417,34 @@ const TxIcon = ({ txType, extra }) => { ); }; -const TxDetail = ({ displayInfo, network }) => { +const TxDetail = ({ + displayInfo, + runTxVerification, + mithril, + mithrilState, + network, +}) => { const capture = useCaptureEvent(); + const [localMithrilState, setLocalMithrilState] = React.useState(); + const [localVerificationData, setLocalVerificationData] = React.useState(); const colorMode = { extraDetail: useColorModeValue('black', 'white'), }; + const mithrilModalRef = useRef(); + const activeMithrilState = localMithrilState ?? mithrilState; + const activeMithrilData = localVerificationData ?? mithril; + + console.log('activeMithrilData', activeMithrilData); + console.log( + 'activeMithrilData?.latest_block_number', + activeMithrilData?.latest_block_number, + typeof activeMithrilData?.latest_block_number + ); + console.log('mithrilModalRef', mithrilModalRef.current); return ( <> + { + + + + Mithril + + + {activeMithrilState !== 'done' && ( + <>{activeMithrilState ?? 'Verifying'}... + )} + {activeMithrilData && activeMithrilState === 'done' && ( + <> + Certificate: {activeMithrilData.certificate_hash} + + Block: {activeMithrilData.latest_block_number.toString()} + + + )} + + {/* */} + + + { + // capture(Events.ActivityActivityDetailTransactionHashClick); + // }} + > + + + + + + + + {/* second col */} + + + {displayInfo.extra.length > 0 ? (