WASM Smart Contracts per Blockchains basate su Substrate
Introduzione
Substrate è un framework open source distribuito da Parity, il team che ha progettato e sviluppato Polkadot. In particolare, Polkadot stesso è stato implementato sfruttando le caratteristiche estendibili e modulari del framework appena citato. L’obiettivo è quello di rendere “semplice” lo sviluppo di blockchains tra loro interoperabili, senza progettare tutto partendo da zero. Il framework è costruito sopra Rust, un linguaggio di programmazione efficiente, affidabile e sicuro, dando delle garanzie a tempo di compilazione prive in altri linguaggi. Queste caratteristiche lo rendono ideale per lo sviluppo di sistemi Blockchain. Un altro componente fondamentale per lo sviluppo di Substrate è libp2p, libreria utile ad integrare funzionalità peer to peer per il corretto funzionamento dei nodi della rete.
Tuttavia, di per sé, Substrate non permette lo sviluppo di smart contracts, ha chiaramente un altro obiettivo. Nel mondo Polkadot, tutte le parachains sono basate fortemente su Substrate, alla luce di ciò, viene spontaneo porsi la seguente domanda: come sviluppare Smart Contracts in un ecosistema dipendente da Substrate ?
Il seguente articolo ha come obiettivo quello di rispondere a questa domanda.
Substrate
Come già accennato, Substrate è il framework che offre delle interfacce per implementare blockchains con una propria logica di business, offrendo un importante grado di astrazione che permette a chi approccia lo sviluppo di sistemi Blockchains di concentrarsi sulla logica applicativa piuttosto che su aspetti di basso livello comuni a tutte le blockchains.
Le chains che hanno Substrate alla base, sfruttano i Pallets, componenti modulari di facile estendibilità che offrono delle interfacce di base.
Contracts Pallet
Tornando al topic, per lo sviluppo di Smart Contracts, Substrate offre un modulo apposito, il contracts pallet. Pertanto, tutti i layer 1 scritti in Substrate possono integrare, in modo custom, gli smart contracts. Si precisa che, da un punto di vista puramente a livelli, gli smart contracts sono a livello 3; Polkadot si pone al primo e la Parachain al secondo. Non è possibile sviluppare applicazioni basate su smart contracts direttamente su Polkadot, sono le Parachains che, tramite il contracts pallet, offrono quest’ulteriore grado di astrazione. Questa architettura permette una migliore distribuzione di responsabilità, oltre a introdurre un ulteriore grado di sicurezza.
Web Assembly e WASM Smart Contracts
Web Assembly assume un ruolo centrale nell’ecosistema Substrate e quindi, nel mondo Polkadot. Esso rappresenta un formato “binario” per eseguire codice ad alte prestazioni su diverse piattaforme, compresi i browser web. E’ il target di compilazione, direttamente eseguibile, dei sorgenti Rust. Alla luce di ciò, i programmi eseguibili generati dal webassembly sono veloci ed efficienti, in quanto non fanno uso di interpreti o altre “strutture” che rallentano l’esecuzione del sistema. L’aspetto più interessante è che è possibile compilare in webassembly diversi linguaggi di programmazione, non solo Rust. A tempo di esecuzione quello che conta non è il linguaggio sorgente, ma il web assembly generato.
Il punto fondamentale è che gli smart contracts afferenti alle Parachains scritte in Substrate devono essere tutti compilati in webassembly, è quest’ultimo che sarà direttamente eseguito e “prelevato” a tempo di esecuzione.
Il messaggio che si vuole trasmettere è che un approccio di questo tipo distrugge le barriere tra Web2 e Web3, perché consente agli sviluppatori di utilizzare il linguaggio di programmazione preferito. Alla fine, sarà sempre generato ed eseguito il webassembly, compatibile con molteplici architetture. In particolare, sarà possibile scrivere smart contracts basati su WASM fintanto che esista un compilatore che traduca il linguaggio di alto livello nel blob WASM.
INK!
Ad oggi il linguaggio di programmazione utilizzato, compatibile con il contracts pallet di Substrate, si chiama ink! Quest’ultimo non è altro che puro Rust con qualche modifica che lo rende prestante per scrivere applicazioni basate su Smart Contracts.
Non è stato necessario reinventare nulla, si è “semplicemente” esteso Rust, compatibile come target di compilazione per webassembly. Si tiene a precisare che ink si basa su tutti i concetti fondamentali descritti nel paragrafo precedente.
Inoltre, poiché scritto in Rust, gli sviluppatori potranno accedere a tutti i tools e le librerie afferenti all’ecosistema Rust, velocizzando i tempi di sviluppo.
Un semplice esempio
In questo esempio si può osservare un semplice smart contract scritto in Ink, la dicitura in bianco è semplice Rust, mentre la parte in viola sono le “estensioni” per renderlo compatibile come linguaggio per smart contracts. Commentiamo la struttura:
-
#(ink(contract)]:
Tramite la seguente dicitura si sta comunicando al compilatore la volontà di scrivere uno smart contract, pertanto il codice sottostante sarà la logica applicativa del contract.
-
#[ink(storage)]
Indica la presenza di una struttura dati utile alla memorizzazione permanente di dati su cui lo smart contract può agire. In sostanza, la base di dati dello smart contract.
-
#[ink(constructor)]
Questo attributo indica la presenza di un costruttore per lo smart contract, sarà il codice relativo all’istanziazione di quest’ultimo.
-
#[ink(message)]
Indica l’inizio di una funzione che sarà possibile invocare dall’esterno che agirà sulla logica dello smart contract, può essere sia in lettura che in scrittura. Le scritture determinano un cambiamento di stato, pertanto sarà necessario firmare una transazione che andrà in input ad un opportuna funzione di transizione.
Tutti gli smart contracts scritti in ink dovranno avere almeno un messaggio e uno storage. La logica del contract illustrata è molto semplice: viene memorizzato un valore booleano (true o false) nello storage. Per interagire con il valore lo smart contract offre due funzioni: get e flip.
- get(): Permette di leggere il valore memorizzato.
- flip(): Permette di modificare il valore booleano, impostandolo all’opposto di quello memorizzato. Questa operazione comporta una transazione on-chain, in quanto determina un cambiamento di stato.
Hands on Practice
Vediamo, in pratica, come fare il deploy di uno smart contract scritto in ink!
Il codice sorgente sarà lo stesso di quello discusso sopra. Come parachain che ospiterà nel runtime lo smart contract, prendiamo Astar, una delle parachain più avanti nello sviluppo di smart contract basati su WASM.
Per i prossimi step si assume che abbiate la toolchain di Rust, di seguito potete installarla:
https://doc.rust-lang.org/cargo/getting-started/installation.html
Step 1
Installare cargo-contract
Questo tool permette, direttamente dal terminale, di avere un template ready-to-go per il nostro smart contract e non solo; permette di interagire e richiamare le funzioni scritte del contract.
Si procede con l’ installazione dal seguente link: https://github.com/paritytech/cargo-contract.git
Step 2
Set up dello smart contract
cargo contract new testcontract
Tramite questo comanda si inizializza un progetto cargo (package manager dell’ecosistema Rust) con tutte le dipendenze per smart contract basati su ink.
Nella directory test contract ci saranno due files
- Cargo.toml che presenta tutta la configurazione utile al progetto, con tutte le dipendenze
- lib.rs è dove risiede il codice applicativo dello smart contract, apriamolo !
Un lettore conoscitore dell’ecosistema Rust, potrà notare la similitudine con progetti Rust
File lib.rs
Il codice è esattamente quello descritto precedentemente. Al di sotto, se aprite il file, ci sono dei test di base per lo smart contract, qui non vengono illustrati perchè vanno oltre allo scopo di questo articolo.
Step 3
Build dello smart contract
A questo punto è il momento di generare i compilati webassembly direttamente eseguibili, tramite il comando:
cargo contract build
Alla fine del processo di compilazione, nella cartella target verranno generati tutti i compilati di cui è possibile fare il deploy direttamente su Astar.
In particolare nella cartella target/ink ci sarà un file testcontract.contract.
Questo file conterrà il blob webassembly e delle informazioni sullo smart secondo l’interfaccia json.
Teniamo traccia del file per fare il deploy correttamente.
Step 4
Deploy su Astar
Per fare il deploy è possibile interagire con un interfaccia grafica sviluppata su browser web. Basterà collegarsi al seguente link: https://contracts-ui.substrate.io/
Si assume che abbiate già un wallet polkadot.js con un account substrate correttamente inizializzato. Nella tendina in alto a sinistra selezionate il network su cui volete effettuare il deploy, per questo tutorial utilizzeremo la testnet di Astar.
Per fare il deploy ed interagire con il contract sarà necessario avere dei testnet tokens che possono essere recuperati direttamente dal portale di Astar https://portal.astar.network/shibuya-testnet/
Basterà collegarsi con polkadot.js al network e cliccare su faucet.
Una volta ottenuti i tokens si procede a fare il deploy tramite Contracts UI, caricando il file testcontract.contract discusso in precedenza.
Seguendo i seguenti step:
- Add new contract
- Upload new contract code
- Selezionare l’account di polkadot.js con i testnet tokens
- Dare un nome allo smart contract
- Caricare il file testcontract.contract
- Next
Alla seguente schermata sarà possibile settare i parametri per l’inizializzazione del contract, per il tutorial lasciamo quelli di default. Infine, clicchiamo nuovamente su next.
A questo punto sarà possibile inizializzare lo smart contract e firmare la transazione. Dopo il tempo di attesa per il deploy (tempo di blocco), potremo interagire con tutte le sue funzionalità, ovvero con la funzione flip e get !