Compare commits
No commits in common. "046eef18e6f192bd03de6257d3f4056ddc3d2898" and "76a1d38e09327b0cbd4975e0630d4af3732b89c2" have entirely different histories.
046eef18e6
...
76a1d38e09
@ -156,15 +156,13 @@ export async function init(): Promise<void> {
|
|||||||
// We connect to all relays now
|
// We connect to all relays now
|
||||||
await services.connectAllRelays();
|
await services.connectAllRelays();
|
||||||
|
|
||||||
await services.updateDeviceBlockHeight();
|
|
||||||
|
|
||||||
// We register all the event listeners if we run in an iframe
|
// We register all the event listeners if we run in an iframe
|
||||||
if (window.self !== window.top) {
|
if (window.self !== window.top) {
|
||||||
await registerAllListeners();
|
await registerAllListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (services.isPaired()) {
|
if (services.isPaired()) {
|
||||||
await navigate('process');
|
await navigate('account');
|
||||||
} else {
|
} else {
|
||||||
await navigate('home');
|
await navigate('home');
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
import { INotification } from '~/models/notification.model';
|
import { INotification } from '~/models/notification.model';
|
||||||
import { IProcess } from '~/models/process.model';
|
import { IProcess } from '~/models/process.model';
|
||||||
import { initWebsocket, sendMessage } from '../websockets';
|
import { initWebsocket, sendMessage } from '../websockets';
|
||||||
import { ApiReturn, Device, HandshakeMessage, Member, MerkleProofResult, NewTxMessage, OutPointProcessMap, Process, ProcessState, RoleDefinition, SecretsStore, UserDiff } from '../../pkg/sdk_client';
|
import { ApiReturn, Device, HandshakeMessage, Member, MerkleProofResult, OutPointProcessMap, Process, ProcessState, RoleDefinition, SecretsStore, UserDiff } from '../../pkg/sdk_client';
|
||||||
import ModalService from './modal.service';
|
import ModalService from './modal.service';
|
||||||
import Database from './database.service';
|
import Database from './database.service';
|
||||||
import { navigate } from '../router';
|
import { navigate } from '../router';
|
||||||
import { storeData, retrieveData, testData } from './storage.service';
|
import { storeData, retrieveData, testData } from './storage.service';
|
||||||
import { BackUp } from '~/models/backup.model';
|
import { BackUp } from '~/models/backup.model';
|
||||||
|
import { PDFDocument, rgb, StandardFonts } from 'pdf-lib';
|
||||||
|
|
||||||
export const U32_MAX = 4294967295;
|
export const U32_MAX = 4294967295;
|
||||||
|
|
||||||
const BASEURL = `http://localhost`;
|
const BASEURL = `https://demo.4nkweb.com`;
|
||||||
const BOOTSTRAPURL = [`${BASEURL}:8090`];
|
const BOOTSTRAPURL = [`${BASEURL}/ws/`];
|
||||||
const STORAGEURL = `${BASEURL}:8081`
|
const STORAGEURL = `${BASEURL}/storage`
|
||||||
const BLINDBITURL = `${BASEURL}:8000`
|
|
||||||
const DEFAULTAMOUNT = 1000n;
|
const DEFAULTAMOUNT = 1000n;
|
||||||
const EMPTY32BYTES = String('').padStart(64, '0');
|
const EMPTY32BYTES = String('').padStart(64, '0');
|
||||||
|
|
||||||
@ -31,7 +31,6 @@ export default class Services {
|
|||||||
private routingInstance!: ModalService;
|
private routingInstance!: ModalService;
|
||||||
private relayAddresses: { [wsurl: string]: string } = {};
|
private relayAddresses: { [wsurl: string]: string } = {};
|
||||||
private membersList: Record<string, Member> = {};
|
private membersList: Record<string, Member> = {};
|
||||||
private currentBlockHeight: number = -1;
|
|
||||||
// Private constructor to prevent direct instantiation from outside
|
// Private constructor to prevent direct instantiation from outside
|
||||||
private constructor() {}
|
private constructor() {}
|
||||||
|
|
||||||
@ -561,7 +560,7 @@ export default class Services {
|
|||||||
const newTip = this.sdkClient.get_txid(parsedMsg.transaction);
|
const newTip = this.sdkClient.get_txid(parsedMsg.transaction);
|
||||||
console.log('Transaction', newTip, 'spends the tip of process', processId);
|
console.log('Transaction', newTip, 'spends the tip of process', processId);
|
||||||
// We take the data out of the output
|
// We take the data out of the output
|
||||||
const newStateId = this.sdkClient.get_opreturn(parsedMsg.transaction);
|
const newStateId = this.sdkClient.get_new_state_id(parsedMsg.transaction);
|
||||||
console.log('newStateId:', newStateId);
|
console.log('newStateId:', newStateId);
|
||||||
// We update the relevant process
|
// We update the relevant process
|
||||||
const updatedProcess = this.sdkClient.process_commit_new_state(process, newStateId, newTip);
|
const updatedProcess = this.sdkClient.process_commit_new_state(process, newStateId, newTip);
|
||||||
@ -575,7 +574,7 @@ export default class Services {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const parsedTx = this.sdkClient.parse_new_tx(newTxMsg, 0, membersList);
|
const parsedTx = this.sdkClient.parse_new_tx(parsedMsg.transaction, 0, membersList);
|
||||||
if (parsedTx) {
|
if (parsedTx) {
|
||||||
try {
|
try {
|
||||||
await this.handleApiReturn(parsedTx);
|
await this.handleApiReturn(parsedTx);
|
||||||
@ -935,7 +934,6 @@ export default class Services {
|
|||||||
async createNewDevice() {
|
async createNewDevice() {
|
||||||
let spAddress = '';
|
let spAddress = '';
|
||||||
try {
|
try {
|
||||||
// We set birthday later when we have the chain tip from relay
|
|
||||||
spAddress = await this.sdkClient.create_new_device(0, 'signet');
|
spAddress = await this.sdkClient.create_new_device(0, 'signet');
|
||||||
const device = this.dumpDeviceFromMemory();
|
const device = this.dumpDeviceFromMemory();
|
||||||
await this.saveDeviceInDatabase(device);
|
await this.saveDeviceInDatabase(device);
|
||||||
@ -954,61 +952,6 @@ export default class Services {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateDeviceBlockHeight(): Promise<void> {
|
|
||||||
if (this.currentBlockHeight === -1) {
|
|
||||||
throw new Error('Current block height not set');
|
|
||||||
}
|
|
||||||
|
|
||||||
let device: Device | null = null;
|
|
||||||
try {
|
|
||||||
device = await this.getDeviceFromDatabase();
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error(`Failed to get device from database: ${e}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!device) {
|
|
||||||
throw new Error('Device not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
const birthday = device.sp_wallet.birthday;
|
|
||||||
if (birthday === undefined || birthday === null) {
|
|
||||||
throw new Error('Birthday not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (birthday === 0) {
|
|
||||||
// This is a new device, so current chain tip is its birthday
|
|
||||||
device.sp_wallet.birthday = this.currentBlockHeight;
|
|
||||||
// We also set last_scan, impossible that we need to scan earlier than this
|
|
||||||
device.sp_wallet.last_scan = this.currentBlockHeight;
|
|
||||||
try {
|
|
||||||
// First set the updated device in memory
|
|
||||||
this.sdkClient.restore_device(device);
|
|
||||||
// Then save it to database
|
|
||||||
await this.saveDeviceInDatabase(device);
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error(`Failed to save updated device: ${e}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// For testing, set last_scan and birthday a few blocks ago
|
|
||||||
device.sp_wallet.last_scan = this.currentBlockHeight - 10;
|
|
||||||
device.sp_wallet.birthday = this.currentBlockHeight - 10;
|
|
||||||
try {
|
|
||||||
this.sdkClient.restore_device(device);
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error(`Failed to restore device: ${e}`);
|
|
||||||
}
|
|
||||||
await this.saveDeviceInDatabase(device);
|
|
||||||
// This is existing device, we need to catch up if last_scan is lagging behind chain_tip
|
|
||||||
if (device.sp_wallet.last_scan < this.currentBlockHeight) {
|
|
||||||
// We need to catch up
|
|
||||||
await this.sdkClient.scan_blocks(this.currentBlockHeight, BLINDBITURL);
|
|
||||||
} else {
|
|
||||||
// Up to date, just returns
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async removeProcess(processId: string): Promise<void> {
|
private async removeProcess(processId: string): Promise<void> {
|
||||||
const db = await Database.getInstance();
|
const db = await Database.getInstance();
|
||||||
const storeName = 'processes';
|
const storeName = 'processes';
|
||||||
@ -1379,7 +1322,6 @@ export default class Services {
|
|||||||
try {
|
try {
|
||||||
const handshakeMsg: HandshakeMessage = JSON.parse(parsedMsg);
|
const handshakeMsg: HandshakeMessage = JSON.parse(parsedMsg);
|
||||||
this.updateRelay(url, handshakeMsg.sp_address);
|
this.updateRelay(url, handshakeMsg.sp_address);
|
||||||
this.currentBlockHeight = handshakeMsg.chain_tip;
|
|
||||||
if (this.membersList && Object.keys(this.membersList).length === 0) {
|
if (this.membersList && Object.keys(this.membersList).length === 0) {
|
||||||
// We start from an empty list, just copy it over
|
// We start from an empty list, just copy it over
|
||||||
this.membersList = handshakeMsg.peers_list;
|
this.membersList = handshakeMsg.peers_list;
|
||||||
@ -1757,4 +1699,96 @@ export default class Services {
|
|||||||
|
|
||||||
return await this.updateProcess(process, {}, publicData, null);
|
return await this.updateProcess(process, {}, publicData, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async generateProcessPdf(processId: string, processState: ProcessState): Promise<void> {
|
||||||
|
const pdfDoc = await PDFDocument.create();
|
||||||
|
const page = pdfDoc.addPage([595.28, 841.89]);
|
||||||
|
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
|
||||||
|
const fontBold = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
|
||||||
|
|
||||||
|
const drawText = (text: string, x: number, y: number, opts: { size?: number, bold?: boolean } = {}) => {
|
||||||
|
const fontSize = opts.size || 12;
|
||||||
|
const usedFont = opts.bold ? fontBold : font;
|
||||||
|
page.drawText(text, { x, y, size: fontSize, font: usedFont, color: rgb(0, 0, 0) });
|
||||||
|
};
|
||||||
|
|
||||||
|
let y = 800;
|
||||||
|
|
||||||
|
// Header
|
||||||
|
drawText("Cabinet de Maître Jean Dupont", 50, y, { bold: true });
|
||||||
|
drawText("Notaire à Paris", 50, y -= 15);
|
||||||
|
drawText("12 rue des Archives", 50, y -= 15);
|
||||||
|
drawText("75003 Paris", 50, y -= 15);
|
||||||
|
drawText("Téléphone : 01 23 45 67 89", 50, y -= 15);
|
||||||
|
drawText("Email : contact@notairedupont.fr", 50, y -= 15);
|
||||||
|
|
||||||
|
// Client
|
||||||
|
y -= 30;
|
||||||
|
drawText("Client : Mme Sophie Martin", 50, y);
|
||||||
|
drawText("8 avenue de la Liberté", 50, y -= 15);
|
||||||
|
drawText("69003 Lyon", 50, y -= 15);
|
||||||
|
drawText("Email : sophie.martin@email.com", 50, y -= 15);
|
||||||
|
|
||||||
|
// Title
|
||||||
|
y -= 40;
|
||||||
|
drawText("Certificat de Validation de Données", 50, y, { size: 14, bold: true });
|
||||||
|
|
||||||
|
// Certification paragraph
|
||||||
|
y -= 40;
|
||||||
|
const certText = `Je soussigné, Maître Jean Dupont, notaire à Paris, certifie par la présente que les données suivantes ont été vérifiées et horodatées à l’aide d’une empreinte cryptographique enregistrée sur la blockchain Bitcoin.`;
|
||||||
|
page.drawText(certText, {
|
||||||
|
x: 50, y,
|
||||||
|
size: 11,
|
||||||
|
font,
|
||||||
|
lineHeight: 14,
|
||||||
|
maxWidth: 500
|
||||||
|
});
|
||||||
|
|
||||||
|
// Dossier number
|
||||||
|
y -= 60;
|
||||||
|
drawText("Numéro de dossier : N-2025-0456-PAR", 50, y);
|
||||||
|
|
||||||
|
// Process ID
|
||||||
|
y -= 40;
|
||||||
|
drawText(`Identifiant du process: ${processId.split(':')[0]}`, 50, y);
|
||||||
|
|
||||||
|
// Hash table
|
||||||
|
y -= 40;
|
||||||
|
|
||||||
|
drawText("Nom", 50, y -= 20, { bold: true });
|
||||||
|
drawText("Empreinte cryptographique (SHA-256)", 150, y, { bold: true });
|
||||||
|
|
||||||
|
for (const [label, hash] of Object.entries(processState.pcd_commitment)) {
|
||||||
|
drawText(label, 50, y -= 18);
|
||||||
|
drawText(hash, 150, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the state id as hash total
|
||||||
|
drawText('Ensemble', 50, y -= 18);
|
||||||
|
drawText(processState.state_id, 150, y);
|
||||||
|
|
||||||
|
// Transaction
|
||||||
|
y -= 40;
|
||||||
|
drawText("Transaction enregistrée sur la blockchain Bitcoin :", 50, y);
|
||||||
|
drawText(processState.commited_in, 50, y -= 15);
|
||||||
|
|
||||||
|
// Date & signature
|
||||||
|
y -= 50;
|
||||||
|
drawText("Fait à Paris, le 10 juin 2025.", 50, y);
|
||||||
|
drawText("Généré automatiquement par lecoffre.io", 50, y -= 30);
|
||||||
|
// drawText("Signature du notaire : ___________________________", 50, y -= 30);
|
||||||
|
|
||||||
|
const pdfBytes = await pdfDoc.save();
|
||||||
|
|
||||||
|
// Download
|
||||||
|
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = `Certificat_Validation_${processId.slice(0,8)}-${processState.state_id.slice(0,8)}.pdf`;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user