ihm_client/src/pages/account/document-validation.ts

322 lines
9.8 KiB
TypeScript

// import { ProcessState } from '../../../pkg/sdk_client';
// import Services from '../../services/service';
// interface State {
// file: File | null;
// fileHash: string | null;
// certificate: ProcessState | null;
// commitmentHashes: string[];
// }
// export interface Vin {
// txid: string; // The txid of the previous transaction (being spent)
// vout: number; // The output index in the previous tx
// prevout: {
// scriptpubkey: string;
// scriptpubkey_asm: string;
// scriptpubkey_type: string;
// scriptpubkey_address: string;
// value: number;
// };
// scriptsig: string;
// scriptsig_asm: string;
// witness: string[];
// is_coinbase: boolean;
// sequence: number;
// }
// export interface TransactionInfo {
// txid: string;
// version: number;
// locktime: number;
// vin: Vin[];
// vout: any[];
// size: number;
// weight: number;
// fee: number;
// status: {
// confirmed: boolean;
// block_height: number;
// block_hash: string;
// block_time: number;
// };
// }
// export function getDocumentValidation(container: HTMLElement) {
// const state: State = {
// file: null,
// fileHash: null,
// certificate: null,
// commitmentHashes: []
// }
// container.innerHTML = '';
// container.style.cssText = `
// display: flex;
// flex-direction: column;
// justify-content: center;
// align-items: center;
// min-height: 100vh;
// gap: 2rem;
// `;
// function createDropButton(
// label: string,
// onDrop: (file: File, updateVisuals: (file: File) => void) => void,
// accept: string = '*/*'
// ): HTMLElement {
// const wrapper = document.createElement('div');
// wrapper.style.cssText = `
// width: 200px;
// height: 100px;
// border: 2px dashed #888;
// border-radius: 8px;
// display: flex;
// flex-direction: column;
// align-items: center;
// justify-content: center;
// cursor: pointer;
// font-weight: bold;
// background: #f8f8f8;
// text-align: center;
// padding: 0.5rem;
// box-sizing: border-box;
// `;
// const title = document.createElement('div');
// title.textContent = label;
// const filename = document.createElement('div');
// filename.style.cssText = `
// font-size: 0.85rem;
// margin-top: 0.5rem;
// color: #444;
// word-break: break-word;
// text-align: center;
// `;
// wrapper.appendChild(title);
// wrapper.appendChild(filename);
// const updateVisuals = (file: File) => {
// wrapper.style.borderColor = 'green';
// wrapper.style.background = '#e6ffed';
// filename.textContent = file.name;
// };
// // === Hidden file input ===
// const fileInput = document.createElement('input');
// fileInput.type = 'file';
// fileInput.accept = accept;
// fileInput.style.display = 'none';
// document.body.appendChild(fileInput);
// fileInput.onchange = () => {
// const file = fileInput.files?.[0];
// if (file) {
// onDrop(file, updateVisuals);
// fileInput.value = ''; // reset so same file can be re-selected
// }
// };
// // === Handle drag-and-drop ===
// wrapper.ondragover = e => {
// e.preventDefault();
// wrapper.style.background = '#e0e0e0';
// };
// wrapper.ondragleave = () => {
// wrapper.style.background = '#f8f8f8';
// };
// wrapper.ondrop = e => {
// e.preventDefault();
// wrapper.style.background = '#f8f8f8';
// const file = e.dataTransfer?.files?.[0];
// if (file) {
// onDrop(file, updateVisuals);
// }
// };
// // === Handle click to open file manager ===
// wrapper.onclick = () => {
// fileInput.click();
// };
// return wrapper;
// }
// const fileDropButton = createDropButton('Drop file', async (file, updateVisuals) => {
// try {
// state.file = file;
// updateVisuals(file);
// console.log('Loaded file:', state.file);
// checkReady();
// } catch (err) {
// alert('Failed to drop the file.');
// console.error(err);
// }
// });
// const certDropButton = createDropButton('Drop certificate', async (file, updateVisuals) => {
// try {
// const text = await file.text();
// const json = JSON.parse(text);
// if (
// typeof json === 'object' &&
// json !== null &&
// typeof json.pcd_commitment === 'object' &&
// typeof json.state_id === 'string'
// ) {
// state.certificate = json as ProcessState;
// state.commitmentHashes = Object.values(json.pcd_commitment).map((h: string) =>
// h.toLowerCase()
// );
// updateVisuals(file);
// console.log('Loaded certificate, extracted hashes:', state.commitmentHashes);
// checkReady();
// } else {
// alert('Invalid certificate structure.');
// }
// } catch (err) {
// alert('Failed to parse certificate JSON.');
// console.error(err);
// }
// });
// const buttonRow = document.createElement('div');
// buttonRow.style.display = 'flex';
// buttonRow.style.gap = '2rem';
// buttonRow.appendChild(fileDropButton);
// buttonRow.appendChild(certDropButton);
// container.appendChild(buttonRow);
// async function checkReady() {
// if (state.file && state.certificate && state.commitmentHashes.length > 0) {
// // We take the commited_in and all pcd_commitment keys to reconstruct all the possible hash
// const fileBlob = {
// type: state.file.type,
// data: new Uint8Array(await state.file.arrayBuffer())
// };
// const service = await Services.getInstance();
// const commitedIn = state.certificate.commited_in;
// if (!commitedIn) return;
// const [prevTxid, prevTxVout] = commitedIn.split(':');
// const processId = state.certificate.process_id;
// const stateId = state.certificate.state_id;
// const process = await service.getProcess(processId);
// if (!process) return;
// // Get the transaction that comes right after the commited_in
// const nextState = service.getNextStateAfterId(process, stateId);
// if (!nextState) {
// alert(`❌ Validation failed: No next state, is the state you're trying to validate commited?`);
// return;
// }
// const [outspentTxId, _] = nextState.commited_in.split(':');
// console.log(outspentTxId);
// // Check that the commitment transaction exists, and that it commits to the state id
// const txInfo = await fetchTransaction(outspentTxId);
// if (!txInfo) {
// console.error(`Validation error: Can't fetch new state commitment transaction`);
// alert(`❌ Validation failed: invalid or non existent commited_in for state ${stateId}.`);
// return;
// }
// // We must check that this transaction indeed spend the commited_in we have in the certificate
// let found = false;
// for (const vin of txInfo.vin) {
// if (vin.txid === prevTxid) {
// found = true;
// break;
// }
// }
// if (!found) {
// console.error(`Validation error: new state doesn't spend previous state commitment transaction`);
// alert('❌ Validation failed: Unconsistent commitment transactions history.');
// return;
// }
// // set found back to false for next check
// found = false;
// // is the state_id commited in the transaction?
// for (const vout of txInfo.vout) {
// console.log(vout);
// if (vout.scriptpubkey_type && vout.scriptpubkey_type === 'op_return') {
// found = true;
// } else {
// continue;
// }
// if (vout.scriptpubkey_asm) {
// const hash = extractHexFromScriptAsm(vout.scriptpubkey_asm);
// if (hash) {
// if (hash !== stateId) {
// console.error(`Validation error: expected stateId ${stateId}, got ${hash}`);
// alert('❌ Validation failed: Transaction does not commit to that state.');
// return;
// }
// }
// }
// }
// if (!found) {
// alert('❌ Validation failed: Transaction does not contain data.');
// return;
// }
// // set found back to false for next check
// found = false;
// for (const label of Object.keys(state.certificate.pcd_commitment)) {
// // Compute the hash for this label
// console.log(`Computing hash with label ${label}`)
// const fileHex = service.getHashForFile(commitedIn, label, fileBlob);
// console.log(`Found hash ${fileHex}`);
// found = state.commitmentHashes.includes(fileHex);
// if (found) break;
// }
// if (found) {
// alert('✅ Validation successful: file hash found in pcd_commitment.');
// } else {
// alert('❌ Validation failed: file hash NOT found in pcd_commitment.');
// }
// }
// }
// async function fetchTransaction(txid: string): Promise<TransactionInfo> {
// const url = `https://mempool.4nkweb.com/api/tx/${txid}`;
// const response = await fetch(url);
// if (!response.ok) {
// throw new Error(`Failed to fetch outspend status: ${response.statusText}`);
// }
// const outspend: TransactionInfo = await response.json();
// return outspend;
// }
// function extractHexFromScriptAsm(scriptAsm: string): string | null {
// const parts = scriptAsm.trim().split(/\s+/);
// const last = parts[parts.length - 1];
// // Basic validation: must be 64-char hex (32 bytes)
// if (/^[0-9a-fA-F]{64}$/.test(last)) {
// return last.toLowerCase();
// }
// return null;
// }
// }