Blog.

Rust, WebAssembly, React e un MVP


Autore
Andrea Provino
Data
Tempo di lettura
7 minuti
Categoria
Logs

rust-react-webassembly-privacy-preserving-machine-learning

I log sono tornati: in questo post vedremo come combinare tra loro Rust, WebAssembly e React per realizzare un nuovo MVP.

Può anche essere considerato un Proof of concept, ma lo continueremo a chiamare Minimum Viable Product giusto perché MVP mi pare un termine migliore.

Prima che tu decida di continuare nella lettura di questo post, devi però conoscere alcune cose.

Sarò estremamente schietto con te.

Sono sempre molto attento al modo attraverso cui scrivo in questo blog.

È mia premura comunicare informazioni complesse al meglio delle mie capacità, semplificando concetti difficili in paragrafi sintetici e utili.

Tuttavia, esiste un format che viene meno a queste considerazioni basilari.

Sono i Log.

I Log costituiscono il lavoro grezzo che precede una guida.

Anzi, sono molto di più.

Rappresentano il lavoro immenso che spesso si cela dietro un traguardo apparentemente facile.

Sono il sudore, la rabbia, la disperazione, la desolazione, il fastidio, e alcune volte il lieto fine, di un percorso arduo… quello del conseguimento di un risultato.

Per tale motivo i Log sono anche un flusso di coscienza, in cui accanto alle meticolose descrizioni dei singoli passi compiuti, troverai riflessioni, errori e incomprensioni.

La fine è a volte semplicemente il lascito per un futuro me più in gamba, e certamente capace di superare eventuali blocchi limitanti.

Come si traduce tutto questo per te?

Se deciderai di procedere nella lettura, troverai termini che potresti non capire, decisamente poco chiari, possibili errori logici, ritrattamenti e molto altro.

A te la scelta.

Ti aspetto sotto perché so che, in fondo, se stai leggendo queste stesse parole, la decisione l’hai già presa.

MVP: WebAssembly, Rust e React

Questa è la fase iniziale di un progetto per la realizzazione di un MVP che implementa codice Rust, per l’efficienza di esecuzione, in un’applicazione React, per la flessibilità e rapidità di realizzazione, grazie a WebAssembly.

Il contesto di riferimento è quello del Privacy Preserving Machine Learning, anche se vagamente a esso riconducibile.

Rust

Ho installato Rust qualche giorno fa. È un linguaggio di programmazione che ho conosciuto di recente, ma con il quale non ho mai programmato.

Pare abbia un curva di apprendimento ripida, simile a quella di C/C++.

Ha tuttavia il grande vantaggio di poter ottimizzare il codice in termini computazionali, una gestione più sicura della memoria e una vasta collezione di librerie mantenute dalla community.

Lo abbiamo scelto perché Facebook ha sviluppato una libreria di Private Set Intersection molto efficiente.

Intendiamo far funzionare quella libreria in un browser web.

Per questo progetto è tuttavia richiesta una funziona instabile di Rust, ergo per cui dobbiamo passare alla versione nightly.

Lo facciamo con il comando:

rustup update nightly

Che eseguiamo in ambiente linux, in WSL 2.

Ora diamo il secondo comando:

rustup target add wasm32-unknown-unknown --toolchain nightly

In pratica stiamo installando un pacchetto per compilare su quasi ogni macchina e eseguire su quasi ogni macchina. (unknown-unknown)

cargo install --git https://github.com/alexcrichton/wasm-gc

Ora installiamo un altra libreria e abbiamo il GO per installare rustify

yarn add rustify

Lo facciamo all’interno di un progetto react, appena inizializzato con:

npx create-react-app react-psi

Ottimo.

What’s next?

Io direi che per iniziare, dato che il miglior modo di procedere in un progetto in cui stai imparando a fare qualcosa di nuovo è procedere a piccoli passi, potremmo creare due contatori.

Da 0 a 100mld, scritti rispettivamente in plan javascript e in Rust.

Poi confrontiamo i tempi di esecuzione per capire il motivo per cui ci stiamo complicando così tanto la vita.

Passiamo a react

React

React è un framework js per lo sviluppo di applicazioni web.

Definiamo allora una funzione contatore triggerata da un pulsante.

Nel frattempo eliminiamo anche il codice boilerplate.

Dunque, js impiega 400ms circa a raggiungere il miliardo, e 9 secondi ai 10 miliardi.

Per i 100 miliardi non ho intenzione di aspettare più di 10 secondi, quindi per il momento diciamo impieghi molto.

Ora configuriamo Rust.

Prima però inizializzo la repositioy latrimenti git impazzisce con 5k file di cui deve tener traccia per chissà quale motivo.

Riavviato anche Visual Studio Code e ci siamo.

Dobbiamo definire un nuovo file counter.rs

#[no_mangle]
pub fn do_loop() -> u32 {
    let mut i: u32 = 0;
    let max = 1000000000000;
    loop {
        i += 1;
        if i >= max {
            return i;
        }
    }
}

Ora prendiamo il wrapper di rustify e importiamo il file.

Abbiamo fatto cose molto turche, e questo è il risultato:

failed TypeError: WebAssembly.instantiate(): Argument 0 must be a buffer sourc

Risolviamolo.

Flash forward di 8 ore e, dopo una serena dormita, eccoci tornati a realizzare l’impossibile.

Vediamo come correggere il problema.

Dobbiamo capire se l’importazione della funzione di rust non vada a buon fine nel formato errato. Potrebbe essere necessario convertire la funzione in wasm, e poi importarla in react.

Procediamo.

Molto bene.

Avevamo una guida aperta, nelle tab di chrome, relativa a questo stesso task.

È un po lunga, ma penso sia necessario procedere così da avere react predisposto a gestire qualsivoglia funzione di Rust.

Inizializziamo allora una nuova cartella per l’applicazione Rust con:

cargo new rust-utils

Questo comando definisce un file Cargo.toml, da cui verifichiamo le dipendenze e specifichiamo i dettagli del progetto.

Abbiamo bisogno di una libreria chiamata wasm-bindgen, che può compilare il nostro codice in WebAssembly.

Spostiamo quindi il nostro counter all’interno del nuovo progetto, e inseriamo quello che mi pare un decoratore ma che in realtà si chiama macro.

Convertiamo tutto in wasm

WebAssembly: Rust to WASM

Abbiamo bisogno di una libreria chiamata wasm-pack, per creare un’interfaccia tra JS e TS, e Rust.

WebAssembly sarà allora lo spazio in cui il codice Rust, opportunamente convertito, potrà essere eseguito.

Lanciamo allora il comando

cargo install wasm-pack

Subito dopo:

wasm-pack build

Crea una cartella pkg, perché non abbiamo specificato alcun nome nel comando precedente.

Per il momento va bene così.

Aspetta un secondo, non mi sono accorto di un errore.

Nella console pare qualcosa sia andato male.

failed to execute wasm-opt:

È un bug. Aggiungiamo queste righe al file Cargo.toml e presguiamo.

[package.metadata.wasm-pack.profile.release]
wasm-opt = ["-Oz", "--enable-mutable-globals"]

WebPack

Dovremmo esserci.

Ora non ci resta che impostare React affinché carichi e gestisca WebAssembly.

Procediamo.

Aggiungiamo la cartella di build come dipendenza in react, grazie al fatto che sia stato generato anche un file package.json

"wasm": "file:./rust-utils/pkg"

Installiamo la dipendenza.

La importiamo in modo asincrono usando la sintassi con promise.

Ma riceviamo un errore:

./node_modules/wasm/rust_utils_bg.wasm
Module parse failed: magic header not detected
File was processed with these loaders:
 * ./node_modules/file-loader/dist/cjs.js
You may need an additional loader to handle the result of these loaders.
Error: magic header not detected

Dobbiamo configurare Webpack per gestire WASM.

Ci serve wasm-loader

Aggiungiamolo come dipendenza dev e.. puf! Lo avevamo già installato. Non so quando.

Ora, non abbiamo un file webpack.

Questo significa due cose:

  • dobbiamo crearne uno
  • dobbiamo dire a npm o yarn di averlo creato e usarlo per avviare il progetto.

Iniziamo con il creare il file.

Un semplice module exports, con due regole per il parsing dei file wasm.

A queste però ne aggiungiamo una per evitare che i file WASM sia gestiti sia dal file-loader che dal wasm-loader.

Benissimo, mi sono accorto che non avevamo installato wasm-loader come dipendeza dev, ma con un solo trattino avevamo semplicemente fatto stampare la versione di yarn

in pratica anziché

yarn add –dev wasm-loader

ho dato il comandp

yarn add -dev wasm-loader

Comunque, abbiamo fatto un po’ di casino. Puliamo tutto e reinstalliamo da zero le dipendenze.

Il problema non è legato alle dipendenze ma a webpack, più precisaimente carichiamo i file wasm con il lodeader di default anziché wasm-loader.

Tuttavia non è facile fare l’ovverride delle configurazioni di webpack.

Procediamo con più insistenza.

Dopo aver tentato di modificare senza successo la configurazione di webapck con una libreria esterna, abbiamo deciso di procedere con l’espulsione delle configurazioni così da avere più autonomia. Effettuiamo allora le modifiche ripulendo il package.json dalle dipendenze inutilizzate.

E ci siamo riusciti!!!

Il modulo è stato correttamente importato.

Ora, procediamo a usare la nostra funzione counter.

Et voila.

Per contare fino a 10 mld javascript impiega 10 secondi circa, mentre Rust 1ms.

Ora capisci perché abbiamo fatto questa fatica.

Cosa ci resta da fare?

Importare la libreria Private-ID di facebook e far funzionare il protocollo di private set intersection.

Per il momento è tutto.

Per aspera, ad astra.

Un caldo abbraccio, Andrea

Taggedlogsweb development


Ultimi post

Patricia Merkle Trie

Il Practical Algorithm To Retrieve Information Coded In Alphanumeric Merkle Trie, o Patricia Merkle Trie è una struttura dati chiave-valore usatada Ethereum e particolarmente efficiente per il salvataggio e la verifica dell’integrità dell’informazione. In questo post ne studieremo le caratteristiche. Prima di procedere, ci conviene ripassare l’introduzione al Merkle Tree nella quale abbiamo chiarito il […]

Andrea Provino
ethereum-patricia-merkle-tree
Tree Data Structure: cos’è un Merkle Tree

Un Merkle Tree è una struttura dati efficiente per verificare che un dato appartenga a un insieme esteso di elementi. È comunemente impiegato nelle Peer to Peer network in cui la generazione efficiente di prove (proof) contribuisce alla scalabilità della rete. Capire i vantaggi di questa struttura ci tornerà utile nel nostro percorso di esplorazione […]

Andrea Provino
merkle-tree-cover
UTXO: come funziona il modello Unspent Transaction Outputs

Per tenere traccia dei bilanci utente, la blockchain di Bitcoin sfrutta un modello di contabilità definito UTXO o Unspent Transaction Outputs. In questo articolo ne esaminiamo le caratteristiche. Ogni blockchain è dotata di un sistema di contabilità, un meccanismo attraverso cui tenere traccia dei bilanci di ciascun utente. I due grandi modelli di riferimento nel […]

Andrea Provino
bitcoin-utxo
Cos’è Ethereum

Possiamo definire Ethereum come una macchina a stati distribuita che traccia le transizioni di un archivio dati general-purpose (i.e. una memoria in grado di registrare qualsiasi dato esprimibile come coppia di chiave e valore o key-value) all’interno della Ethereum Blockchain. È arrivato il momento di esplorare uno dei progetti tecnologici più innovativi e interessanti degli […]

Andrea Provino
ethereum