Move è nato nelle prime fasi del progetto Libra nel 2018: i due fondatori di Mysten (Evan ed io) erano anche il team fondatore di Libra. Prima di decidere di creare un nuovo linguaggio, il primo team di Libra ha studiato approfonditamente i casi d'uso e i linguaggi degli smart contract esistenti per capire cosa volevano fare gli sviluppatori e dove i linguaggi esistenti non funzionavano. Il problema chiave che abbiamo scoperto è che i contratti intelligenti riguardano esclusivamente risorse e controllo degli accessi, ma i primi linguaggi dei contratti intelligenti mancavano di rappresentazione del tipo/valore per entrambi. La nostra ipotesi è che se forniamo astrazioni di prima classe per questi concetti chiave, possiamo migliorare notevolmente la sicurezza dei contratti intelligenti e la produttività dei programmatori di contratti intelligenti: avere il vocabolario giusto per il compito da svolgere può fare la differenza. Nel corso degli anni, molte persone hanno contribuito alla progettazione e all'implementazione di Move mentre il linguaggio si evolveva da un'idea chiave a un linguaggio di contratto intelligente indipendente dalla piattaforma con l'audace obiettivo di diventare il "JavaScript del web3".
Oggi siamo entusiasti di annunciare una pietra miliare nell’integrazione di Move e Sui. La funzionalità di Sui Move è completa, supportata da strumenti avanzati e dispone di ampia documentazione ed esempi, incluse le seguenti sezioni:
Una serie di tutorial sulla programmazione utilizzando gli oggetti Sui Move. Un documento di sviluppo sulle conoscenze di base, modelli di progettazione ed esempi di Sui Move Un plug-in di miglioramento VSCode sviluppato dal team Mysten Move, che supporta l'analisi del codice e la diagnosi degli errori e si integra la costruzione, il test e la gestione dei pacchetti di Move, la generazione della documentazione e il validatore Move integrato con sui CLI
Ciò che rende Move unico
Move è un linguaggio incorporato multipiattaforma. La sintassi di base in sé è molto semplice: ha concetti comuni come strutture, numeri interi e indirizzi, ma non ha concetti specifici della blockchain come conti e transazioni), tempo, crittografia, ecc. Queste funzionalità devono essere fornite dalla piattaforma blockchain integrata con Move. È importante sottolineare che queste blockchain non richiedono i propri Move fork: ciascuna piattaforma utilizza la stessa macchina virtuale Move, validatore di bytecode, compilatore, validatore, gestore di pacchetti e CLI, ma basandosi su Code su questi componenti principali per aggiungere funzionalità specifiche della blockchain . Diem è stata la prima blockchain a incorporare Move, e le successive blockchain basate su Move (tra cui 0L, StarCoin e Aptos) hanno adottato per lo più un approccio in stile Diem. Sebbene Move in stile Diem abbia alcune grandi qualità, sia la natura autorizzata di Diem che alcuni dettagli di implementazione della blockchain Diem (in particolare il modello di archiviazione) rendono difficili da implementare alcuni casi d’uso di base del contratto intelligente. In particolare, i design originali di Move e Diem sono antecedenti all’esplosione di popolarità degli NFT e presentano alcune stranezze che rendono particolarmente complicata l’implementazione dei casi d’uso legati agli NFT. In questo post, dimostreremo i problemi con l'incorporamento originale del Move in stile Diem attraverso tre esempi simili e descriveremo come abbiamo risolto questo problema in Sui Move. Presumiamo una conoscenza di base di Move, ma speriamo che questi punti chiave siano comprensibili a chiunque abbia un background di programmazione.
Esperienza fluida durante la creazione di risorse su larga scala
La capacità di creare e distribuire risorse in blocco è fondamentale sia per l'onboarding che per il coinvolgimento degli utenti web3. Forse uno streamer Twitch vuole distribuire NFT commemorativi, un creatore vuole inviare biglietti per un evento speciale o uno sviluppatore di giochi vuole lanciare nuovi oggetti a tutti i giocatori. Ecco un tentativo (fallito) di scrivere codice per il conio di massa di asset in Move in stile Diem. Questo codice prende come input un vettore di indirizzi dei destinatari, genera una risorsa per ciascun destinatario e tenta di trasferire la risorsa.
In uno spostamento in stile Diem, l'archiviazione globale è tipizzata da coppie (indirizzo, nome del tipo), ovvero ciascun indirizzo può archiviare al massimo una risorsa di un tipo specifico. Pertanto, move_to(receiverient, CoolAsset { ...} tenta di spostare CoolAsset memorizzandolo nell'indirizzo del destinatario. Tuttavia, questo codice non riesce a compilare alla riga move_to(receiverient, ...). La domanda chiave è, in un Move in stile Diem, non è possibile inviare un valore di tipo CoolAsset a un indirizzo A, a meno che una transazione non venga inviata da un indirizzo diverso da A e il proprietario di un account creato in A invii una transazione e scelga esplicitamente di ricevere CoolAsset. tipo di oggetto. Si tratta di due transazioni, solo per ricevere una risorsa! La decisione di farlo ha senso per Diem, che è un sistema autorizzato che deve limitare attentamente la creazione di account ed evitare che gli account vengano limitati dal sistema di archiviazione. Invece di detenere troppe risorse, tuttavia, per un sistema aperto che vuole utilizzare l'allocazione delle risorse come meccanismo di onboarding, o semplicemente consentire in generale alle risorse di fluire liberamente tra gli utenti, come fanno su Ethereum e blockchain simili, è molto limitato .
Il codice per implementare la stessa funzione in Sui Move è il seguente:
La memoria globale di Sui Move è codificata dall'ID oggetto. Ogni struttura con coppie chiave-valore è un "oggetto Sui", che deve avere un campo ID univoco a livello globale. Sui Move introduce una primitiva di trasferimento che può essere utilizzata su qualsiasi oggetto Sui, invece di utilizzare la struttura restrittiva move_to. Sotto il cofano, questa primitiva mappa l'id su un CoolAsset nella memoria globale e aggiunge metadati per indicare che il valore è di proprietà del destinatario. Una proprietà interessante della versione Sui di mass_mint è che viene scambiata con tutte le altre transazioni (comprese le altre transazioni che chiamano mass_mint!). Il runtime Sui se ne accorgerà e invierà transazioni che chiamano questa funzione tramite il "percorso veloce" trasmesso dal consenso bizantino che non richiede consenso. Tali transazioni possono essere impegnate o eseguite in parallelo. Ciò non richiede alcuno sforzo da parte del programmatore (scrivono semplicemente il codice sopra e il runtime gestisce il resto). Forse sottilmente, questo non è il caso della variante Diem di questo codice - anche se il codice sopra funziona, esiste e le chiamate guid::create creeranno punti di conflitto con altre transazioni che generano GUID o toccano le risorse dell'account. In alcuni casi, è possibile riscrivere il codice Move in stile Diem per evitare l'argomento, ma molti dei modi convenzionali di scrivere Move in stile Diem introducono piccoli ostacoli che ostacolano l'esecuzione parallela.
Proprietà e trasferimenti di beni locali
Estendiamo il codice Move in stile Diem con una soluzione alternativa che viene effettivamente compilata ed eseguita. Il modo consueto per farlo è il "modello wrapper": poiché Bob non può spostare CoolAsset direttamente all'indirizzo di Alice, chiediamo ad Alice di "acconsentire" a ricevere CoolAsset, pubblicando prima un tipo wrapper CoolAssetStore, che contiene un tipo Collection (tavolo). Alice può farlo chiamando la funzione opt_in. Aggiungiamo quindi il codice che consente a Bob di spostare CoolAsset dal suo CoolAssetStore al CoolAssetStore di Alice. A questo codice aggiungiamo un'ulteriore novità: consentiremo i trasferimenti di CoolAssets solo se creati almeno 30 giorni fa. Questo tipo di politica è importante per i creatori che (ad esempio) vogliono impedire agli speculatori di acquistare/promuovere i biglietti per eventi in modo che sia più facile per i veri fan ottenere quei biglietti a un prezzo ragionevole.
Questo codice è valido. Ma questo è un modo piuttosto complicato per eseguire il compito di trasferire risorse da Alice a Bob! Diamo un'occhiata a un'altra implementazione di Sui Move
Questo codice è molto più breve. La cosa fondamentale da notare qui è che cool_transfer è una funzione di ingresso (il che significa che può essere chiamata direttamente dal runtime Sui tramite una transazione), tuttavia ha un parametro di tipo CoolAsset come input. Questa è di nuovo la magia di Sui Runtime Una transazione include una serie di ID oggetto su cui vuole operare e quando Sui Runtime:
Analizza l'ID in un valore oggetto (non sono necessarie le parti prestito_global_mut e table_remove nel codice in stile Diem sopra). Controlla se l'oggetto è di proprietà del mittente della transazione (elimina la necessità della sezione signer::address_of e del relativo codice sopra). Questa parte è particolarmente interessante e la spiegheremo presto: In Sui, il controllo sicuro della proprietà dell'oggetto fa parte del runtime. Controlla il tipo del valore dell'oggetto in base al tipo di parametro della funzione chiamata cool_transfer ai parametri cool_transfer e chiamare la funzione
Ciò ha permesso ai programmatori di Sui Move di saltare il modello per la parte della logica "Ritiro" e passare direttamente alla parte divertente: controllare la politica di scadenza a 30 giorni. Allo stesso modo, anche la parte “deposito” è notevolmente semplificata con la struttura di trasferimento Sui Move spiegata sopra. Infine, non è necessario introdurre un tipo di wrapper con una raccolta interna come CoolAssetStore: l'archivio globale Sui indicizzato tramite ID consente a un indirizzo di memorizzare un numero qualsiasi di valori di un determinato tipo. Un'altra differenza da sottolineare è che cool_transfer in stile Diem ha 5 modi per interrompere (ovvero fallisce e addebita il gas all'utente senza completare il trasferimento), mentre cool_transfer Sui Move ha solo 1 modo per interrompere: quando viene violata la politica dei 30 giorni. Scaricare i controlli sulla proprietà degli oggetti nel runtime è una grande vittoria, non solo in termini di ergonomia, ma anche in termini di sicurezza. L'implementazione della sicurezza a livello di runtime previene errori nell'implementazione di questi controlli sul costrutto (o nel dimenticarsene completamente!). Infine, nota che la firma della funzione del punto di ingresso di Sui Move cool_transfer( asset: CoolAsset, ...) ci fornisce molte informazioni su cosa farà questa funzione (è più opaca delle firme delle funzioni in stile Diem). Possiamo pensare che questa funzione richieda il permesso di trasferire CoolAsset, mentre un'altra funzione f(asset: &mut CoolAsset, ...) richiede il permesso di scrivere (ma non trasferire) CoolAsset e g(asset: &CoolAsset, . ..) sta solo chiedendo il permesso di lettura.
Poiché queste informazioni sono disponibili direttamente nella firma della funzione (non è richiesta alcuna esecuzione o analisi statica!), possono essere utilizzate direttamente dai portafogli e da altri strumenti client. In Sui Wallet, stiamo lavorando su richieste di firma leggibili dall'uomo, sfruttando queste firme di funzioni strutturate per fornire agli utenti richieste di autorizzazione in stile iOS/Android. Il portafoglio può dire qualcosa del tipo: "Questa transazione richiede l'autorizzazione per leggere il tuo CoolAsset, scrivere nella tua AssetCollection e trasferire il tuo ConcertTicket. Continuare?".
Le richieste di firma leggibili dall'uomo risolvono un vettore di attacco su larga scala presente su molte piattaforme esistenti, comprese quelle che utilizzano Move! in stile Diem!, dove gli utenti del portafoglio devono firmare ciecamente le transazioni senza comprendere l'impatto che potrebbero avere. Riteniamo che rendere l'esperienza del portafoglio meno rischiosa sia un passo fondamentale nel promuovere l'adozione mainstream dei portafogli di criptovaluta e abbiamo progettato Sui Move per supportare questo obiettivo abilitando funzionalità come richieste di firma leggibili dall'uomo.
Raggruppa risorse diverse
Infine, consideriamo un esempio di raggruppamento di diversi tipi di asset. Questo è un caso d'uso abbastanza comune: i programmatori potrebbero voler raggruppare diversi tipi di NFT in una raccolta, raggruppare articoli per venderli su un mercato o aggiungere allegati a articoli esistenti. Supponiamo di avere la seguente situazione:
Alice definisce un oggetto personaggio da utilizzare nel gioco Alice desidera supportare la decorazione del suo personaggio con diversi tipi di accessori di terze parti che verranno successivamente creati. Chiunque dovrebbe essere in grado di creare un accessorio, ma il proprietario di un personaggio dovrebbe decidere se aggiungerlo. Il trasferimento di un personaggio dovrebbe trasferire automaticamente tutti i suoi accessori.
Questa volta iniziamo con il codice per Sui Move. Trarremo vantaggio da un altro aspetto della funzionalità di proprietà degli oggetti incorporata nel runtime Sui: un oggetto può essere posseduto da un altro oggetto. Ogni oggetto ha un proprietario univoco, ma un oggetto principale può avere un numero qualsiasi di oggetti secondari. La relazione oggetto genitore/figlio viene creata utilizzando la funzione transfer_to_object, che è l'oggetto associato della funzione di trasferimento introdotta sopra.
In questo codice, il modulo del ruolo include una funzione di accessorizzazione che consente al proprietario del ruolo di aggiungere un oggetto accessorio di qualsiasi tipo come oggetto figlio. Ciò consente a Bob e Clarissa di creare i propri tipi di ornamenti con proprietà e funzionalità diverse che Alice non ha, ma si basa su ciò che Alice ha già fatto. Ad esempio, la maglietta di Bob può essere equipaggiata solo se è il colore preferito del personaggio e la spada di Clarissa può essere brandita solo se il personaggio è abbastanza potente. Quella che segue è una dimostrazione pratica del Move in stile Diem, ma nessuno di essi ha avuto successo, ovvero il Move in stile Diem non può realizzare lo scenario sopra descritto.
Si può vedere che i problemi nel Move in stile Diem sono i seguenti
Sono supportate solo le raccolte dello stesso tipo (come ha dimostrato il primo test), ma gli accessori non sono fondamentalmente un tipo di oggetto. La relazione tra oggetti può essere ottenuta solo tramite il "wrapping" (cioè la memorizzazione di un oggetto all'interno di un altro oggetto); la raccolta di oggetti che possono essere racchiusi deve essere predefinita (come nel secondo tentativo), o aggiunta in modo temporaneo, e la composizione di oggetti aggiuntivi non è supportata (come nel terzo test).
Riassumere
Sui è la prima piattaforma a deviare significativamente dal design originale di Diem nel modo in cui viene utilizzato Move. Progettare una combinazione che sfrutti appieno Move e le capacità uniche della piattaforma è sia un'arte che una scienza, che richiede una profonda comprensione del linguaggio Move e delle capacità della blockchain sottostante. Siamo molto entusiasti dei progressi che Sui Move sta facendo e dei nuovi casi d'uso che consentirà! ". [1] Un altro argomento a favore di una politica di spostamento in stile Diem è che "devi aderire prima di poter ricevere un tipo di risorsa specifica". Questo è un buon meccanismo per prevenire lo spam. Tuttavia, pensiamo alla prevenzione dello spam poiché appartiene al livello dell'applicazione, anziché richiedere agli utenti di inviare transazioni che costano denaro reale per aderire alla ricezione di risorse, lo spam può essere facilmente affrontato (ad esempio) a livello di portafoglio con policy dettagliate definite dall'utente e filtri antispam automatizzati. .