// 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 { // 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; // } // }