Scritto da: Beosin
Oggi, con il rapido sviluppo della tecnologia blockchain, TON (The Open Network), in quanto piattaforma blockchain efficiente e flessibile, sta attirando l’attenzione di sempre più sviluppatori. L'architettura e le funzionalità uniche di TON forniscono strumenti potenti e ricche possibilità per lo sviluppo di applicazioni decentralizzate.
Tuttavia, con l’aumento della funzionalità e della complessità, la sicurezza dei contratti intelligenti diventa sempre più importante. Essendo un linguaggio di programmazione per contratti intelligenti su TON, FunC è noto per la sua flessibilità ed efficienza, ma comporta anche molti potenziali rischi e sfide. Per scrivere contratti intelligenti sicuri e affidabili è necessario che gli sviluppatori abbiano una profonda comprensione delle caratteristiche del linguaggio FunC e dei possibili rischi.
Questo articolo analizzerà in dettaglio alcune funzionalità relative ai contratti intelligenti sulla blockchain di TON, nonché i punti di vulnerabilità facilmente trascurati dei contratti intelligenti su TON.
Funzionalità asincrone Ton e analisi del meccanismo degli account
Chiamata asincrona con contratto intelligente
Sharding della rete e comunicazione asincrona
La blockchain TON è progettata in tre tipi di catene: Masterchain, Workingchains e Shardchains.
La catena principale è il nucleo dell'intera rete ed è responsabile della memorizzazione dei metadati e del meccanismo di consenso dell'intera rete. Registra lo stato di tutte le catene di lavoro e delle catene di shard e garantisce la coerenza e la sicurezza dell'intera rete. Le catene di lavoro sono blockchain indipendenti, fino a 2^32 in numero, responsabili dell'elaborazione di tipi specifici di transazioni e contratti intelligenti. Ogni catena di lavoro può avere regole e caratteristiche proprie per soddisfare le diverse esigenze applicative. Le catene di sharding sono sottocatene della catena di lavoro, utilizzate per dividere ulteriormente il carico della catena di lavoro e migliorare le capacità di elaborazione e la scalabilità. Ciascuna catena di lavoro può essere suddivisa in un massimo di 2^60 catene di shard e le catene di shard elaborano alcune transazioni in modo indipendente, ottenendo così un'elaborazione parallela efficiente.
In teoria, ogni account può occupare esclusivamente una catena di shard, ogni account mantiene in modo indipendente il proprio saldo COIN/TOKEN e le transazioni tra ciascun account possono essere completamente parallele. Gli account vengono trasmessi tramite messaggi asincroni. Il percorso dei messaggi trasmessi tra catene di shard è log_16(N) - 1, dove N è il numero di catene di shard.
Fonte immagine: https://frontierlabzh.medium.com/ton-web3 Il weixin-e1d3ae3b3574 del mondo
In Ton, i contratti intelligenti interagiscono inviando e ricevendo messaggi. Questi messaggi possono essere messaggi interni (in generale, messaggi inviati da contratti intelligenti che interagiscono tra loro) o messaggi esterni (messaggi inviati da fonti esterne). Il processo di consegna del messaggio non necessita di attendere una risposta immediata dal contratto di destinazione e il mittente può continuare a eseguire il resto del codice logico. Rispetto alle chiamate sincrone di Ethereum, questo meccanismo di messaggistica asincrona offre maggiore flessibilità e scalabilità, riduce i colli di bottiglia nelle prestazioni causati dall'attesa di risposte e comporta anche sfide nella gestione della concorrenza e delle condizioni di competizione.
Formato e struttura del messaggio
In Ton, il messaggio solitamente contiene il mittente, il destinatario, l'importo, il corpo del messaggio e altre informazioni. Il corpo del messaggio può essere una chiamata di funzione, un trasferimento di dati o altro contenuto personalizzato. Il formato del messaggio utilizzato da Ton può essere definito ed ampliato in modo flessibile, consentendo di trasmettere in modo efficiente vari tipi di informazioni tra diversi contratti.
messaggio della cella = begin_cell()
.store_uint(0x18, 6)
.store_slice(indirizzo)
.store_coins(importo)
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
.store_slice(corpo_messaggio)
.fine_cella();
Coda di messaggi ed elaborazione dello stato
Ogni contratto mantiene una coda di messaggi per archiviare i messaggi non elaborati. Durante l'esecuzione del contratto, esso verrà elaborato uno ad uno in base ai messaggi in coda. Poiché l'elaborazione del messaggio è asincrona, lo stato del contratto non viene aggiornato immediatamente finché non viene ricevuto il messaggio.
Vantaggi della messaggistica asincrona
Meccanismo di sharding efficiente: il meccanismo asincrono di Ton è altamente coerente con il suo design di sharding. Ogni shard elabora i messaggi di contratto e le modifiche di stato in modo indipendente, evitando il problema del ritardo causato dalla comunicazione sincrona tra shard. Questo design migliora il throughput e la scalabilità dell'intera rete.
Ridurre il consumo di risorse: poiché i messaggi asincroni non richiedono una risposta immediata, l'esecuzione del contratto di Ton può essere completata in più blocchi, evitando un consumo eccessivo di risorse all'interno di un singolo blocco. Ciò consente a Ton di supportare contratti intelligenti più complessi e ad alta intensità di risorse.
Tolleranza agli errori e affidabilità: il meccanismo di consegna asincrona dei messaggi rende il sistema più tollerante agli errori. Ad esempio, se un contratto non può rispondere ai messaggi in tempo a causa di vincoli di risorse o per altri motivi, il mittente può comunque continuare a elaborare altra logica e il sistema non verrà bloccato a causa del ritardo di un singolo contratto.
Sfide della progettazione contrattuale asincrona
Problemi di coerenza dello stato: poiché la consegna dei messaggi è asincrona, lo stato del contratto può ricevere messaggi diversi in momenti diversi, il che richiede agli sviluppatori di prestare particolare attenzione ai problemi di coerenza dello stato. Nella progettazione di un contratto è necessario tenere conto dei possibili cambiamenti di stato causati da diverse sequenze di messaggi per garantire che il sistema possa mantenere la coerenza in ogni circostanza.
Race conditions e protezione: l'elaborazione asincrona dei messaggi comporta potenziali problemi di race condition e più messaggi potrebbero tentare di modificare lo stato del contratto contemporaneamente. Gli sviluppatori devono introdurre meccanismi di blocco appropriati o utilizzare operazioni transazionali per prevenire conflitti di stato.
Considerazioni sulla sicurezza: i contratti asincroni sono vulnerabili agli attacchi man-in-the-middle o agli attacchi di riproduzione durante l'elaborazione delle comunicazioni tra contratti. Pertanto, quando si progettano contratti asincroni, è necessario prendere in considerazione questi potenziali rischi per la sicurezza e adottare misure per prevenirli, come l’utilizzo di timestamp, numeri casuali o firme multiple.
Modello di registro
Ton (The Open Network) ha adottato un modello unico di astrazione dei conti e di registro durante la progettazione della sua infrastruttura blockchain. La flessibilità di questo modello si riflette nel modo in cui gestisce lo stato dell'account, la messaggistica e l'esecuzione del contratto.
Astrazione dei conti
Il modello di conto di Ton adotta un'astrazione basata su contratti e ogni conto può essere considerato come un contratto. Questo ha alcune somiglianze con il modello di astrazione del conto di Ethereum, ma è più flessibile e versatile. In Ton, i conti non sono solo contenitori per la detenzione di beni, ma contengono anche il codice del contratto e i dati sullo stato. Ogni account è composto dal proprio codice (Codice), dai dati (Dati) e dalla logica di elaborazione dei messaggi (Gestione dei messaggi).
Struttura dell'account: ogni account Ton ha un indirizzo univoco, che è una combinazione dell'hash del codice dell'account, dei dati iniziali al momento della distribuzione e di alcuni altri parametri. Ciò significa che lo stesso codice e i dati iniziali distribuiti in ambienti diversi (ad esempio, blockchain o frammenti diversi) possono generare indirizzi diversi.
Flessibilità: poiché ogni account può eseguire il proprio codice contratto, gli account di Ton possono implementare una logica molto complessa. I conti non sono solo semplici detentori di saldo, ma possono anche gestire complessi trasferimenti statali, comunicazioni di messaggi tra conti e persino operazioni automatizzate basate su condizioni specifiche. Ciò rende il modello di conto di Ton più scalabile e flessibile rispetto a quelli delle blockchain tradizionali.
Struttura del registro
La struttura del registro di Ton è progettata per gestire in modo efficiente transazioni simultanee su larga scala e supporta la messaggistica asincrona e le operazioni multi-shard. Lo stato di ciascun conto è archiviato in una struttura ad albero Merkle, che consente al registro di Ton di avere efficienti capacità di verifica dello stato.
deposito statale
Le informazioni sullo stato dell'account vengono archiviate in un archivio persistente e organizzate tramite alberi Merkle per garantire l'integrità e la sicurezza dello stato. Questa progettazione consente inoltre query e verifiche efficienti dello stato, soprattutto negli scenari di transazioni tra partizioni.
Lo stato dell'account o del contratto intelligente in genere contiene quanto segue:
Saldo nella valuta di base
Saldi in altre valute
Codice del contratto intelligente (o il suo hash)
I dati persistenti dello smart contract (o il suo Merkle hash)
Statistiche sul numero di unità di archiviazione di persistenza e sul numero di byte grezzi utilizzati
L'ora più recente del pagamento memorizzata in modo persistente dallo smart contract (in realtà il numero di blocco della catena principale)
La chiave pubblica richiesta per trasferire valuta e inviare messaggi da questo account (facoltativo; uguale allo stesso account_id per impostazione predefinita). In alcuni casi, simile a quanto avviene con gli output delle transazioni Bitcoin, qui è possibile trovare un codice di controllo della firma più sofisticato account_id sarà quindi uguale all'hash di questo codice;
Non tutte le informazioni sono richieste per ogni account. Ad esempio, il codice del contratto intelligente funziona solo con i contratti intelligenti, ma non con gli account “semplici”. Inoltre, mentre qualsiasi conto deve avere un saldo diverso da zero nella valuta primaria (ad esempio, la catena principale per la catena di lavoro di base e il Gram per la catena frammentata), altre valute possono avere saldi pari a zero. Per evitare di conservare dati inutilizzati, durante la creazione della catena di lavoro viene definito un tipo di prodotto somma, che utilizza diversi byte marcatori per distinguere diversi "costruttori sufficienti". Infine, lo stato dell'account stesso viene salvato come raccolta di celle per l'archiviazione di persistenza TVM.
Consegna ed elaborazione dei messaggi
La struttura del registro di Ton ha il supporto integrato per la messaggistica asincrona e ciascun account può elaborare in modo indipendente i messaggi ricevuti e aggiornare il proprio stato. Questo meccanismo di messaggistica asincrono consente interazioni complesse tra account senza influire sul normale funzionamento di altri account a causa di ritardi in un'operazione.
Modello a gas
La blockchain Ton (The Open Network) ottimizza notevolmente l'efficienza di esecuzione dei contratti intelligenti attraverso il suo esclusivo modello di tariffa del gas. Il modello della tariffa del gas viene utilizzato nella blockchain per misurare e limitare le risorse consumate durante l’esecuzione dei contratti intelligenti. Rispetto al modello Gas delle blockchain tradizionali (come Ethereum), il design del modello di Ton è più complesso ed efficiente e può gestire in modo più accurato il consumo di risorse durante l'esecuzione del contratto.
Misurazione del consumo di gas raffinato
Il modello Gas di Ton può misurare con precisione le risorse di calcolo, le operazioni di archiviazione e i costi di consegna dei messaggi consumati dai contratti intelligenti durante l'esecuzione. Attraverso una misurazione dettagliata di risorse quali elaborazione, archiviazione e messaggistica, il modello Ton's Gas può impedire che alcune operazioni eccessivamente complesse assorbano troppe risorse. Limitando il consumo di gas, Ton garantisce che ciascun nodo della rete possa allocare equamente le risorse di calcolo ed evitare un consumo eccessivo delle risorse di rete da parte di un singolo contratto o operazione.
Elaborazione parallela e ottimizzazione del gas
Ton supporta l'elaborazione parallela di contratti intelligenti, che consente a più contratti di essere eseguiti contemporaneamente su frammenti diversi senza bloccarsi a vicenda. In questo progetto, il suo modello Gas è strettamente integrato con il suo meccanismo di esecuzione parallela e sharding. Elaborando contratti in parallelo su più shard, Ton può distribuire i calcoli e i pagamenti del Gas su diversi nodi e catene, evitando la congestione della rete, massimizzando al tempo stesso l'utilizzo delle risorse.
Meccanismo di regolazione dinamica del gas
Il modello Ton's Gas prevede un meccanismo di adeguamento dinamico che consente di adeguare la tariffa Gas in base al carico in tempo reale della rete. Ciò significa che quando il carico della rete è basso, gli utenti possono stipulare contratti con tariffe del gas inferiori, incoraggiando così le operazioni durante i periodi di basso carico e bilanciando l’utilizzo delle risorse della rete. Questo meccanismo non solo migliora l’esperienza dell’utente, ma controlla anche il picco di utilizzo delle risorse attraverso un approccio orientato al mercato.
È facile ignorare le scappatoie di molti contratti intelligenti
Nel nostro ultimo articolo di analisi della sicurezza TON, abbiamo introdotto in dettaglio le vulnerabilità generali della sicurezza dell'ecosistema Ton. Puoi anche fare riferimento alla seguente tabella:
Questo articolo si concentrerà sui punti di vulnerabilità facilmente trascurati nel contratto TON riepilogati dal nostro team:
(1) Ottimizzazione della leggibilità del codice
Nei contratti intelligenti di TON, i numeri vengono utilizzati per memorizzare i dati rilevanti inviati dai messaggi. Ad esempio, nel codice seguente, i numeri vengono utilizzati più volte per rappresentare l'identificazione e la durata di archiviazione dei dati corrispondenti, il che riduce notevolmente la leggibilità e la manutenibilità del codice. sesso. Quando altri sviluppatori leggono questo codice, è difficile comprendere il significato e lo scopo di questi numeri. Per migliorare la leggibilità e la manutenibilità del codice, si consiglia di definire valori numerici chiave come costanti denominate, ad esempio: 0x18 è definito come NON_BOUNCEABLE.
check_std_addr(indirizzo);var msg = begin_cell() store_uint(0x18, 6) ;; nobounce store_slice(indirizzo) store_coins(importo) store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) end_cell();send_raw_message(msg, 1);
Inoltre, per il messaggio di errore nelle condizioni di giudizio del contratto, si raccomanda anche di definire variabili corrispondenti per sostituire i codici di errore.
throw_unless(705, equal_slices(indirizzo_proprietario, indirizzo_mittente));
(2) Utilizzare end_parse() per garantire l'integrità dei dati
Nel contratto TON, l'analisi dei dati segue un ordine fisso, caricando gradualmente tipi di dati specificati dai dati originali. Questo metodo di analisi garantisce la coerenza e l'accuratezza dei dati. Come mostrato di seguito:
() load_data() impuro {
fetta ds = get_data().begin_parse();
storage::owner = ds~load_msg_addr();
storage::quantità = ds~load_uint(256);
storage::data = ds~load_ref();
storage::api_data = ds~load_ref();
{NS} = ...
}
Nota che end_parse() qui viene utilizzato per verificare se la sezione di dati (fetta) è vuota. Se la sezione non è vuota, la funzione genererà un'eccezione. Ciò garantisce che il formato e il contenuto dei dati siano quelli previsti. Se la funzione end_parse() rileva che sono rimasti ancora dati nella sezione di dati, ciò potrebbe indicare che l'analisi dei dati non sta andando esattamente come previsto o che c'è un problema con il formato dei dati. Pertanto, chiamando end_parse(), è possibile verificare se sono presenti omissioni o eccezioni nei dati durante il processo di analisi.
(3) Eccezioni causate dalla mancata corrispondenza tra record di dati e tipi di archiviazione
La cosa principale da notare qui è che i tipi di accesso di int e uint corrispondono Nel codice mostrato di seguito, store_int() viene utilizzato per memorizzare il valore del tipo int di -42, ma load_uint() viene utilizzato per caricare questo valore, un. qui può verificarsi un'eccezione.
() Funzione_Test() {
var cella = inizio_cella();
cella = cell.store_int(-42, 32);
var my_cell = cell.end_cell();
fetta s = my_cell.begin_parse();
var risultato = s.load_uint(32);
}
(4) Uso ragionevole di inline_ref e dei modificatori inline
Innanzitutto, dobbiamo spiegare la differenza tra i modificatori inline_ref e inline:
lInline: per le funzioni che utilizzano il modificatore inline, il loro codice verrà inserito direttamente nella posizione chiamante ogni volta che viene chiamato. Cioè, ogni volta che viene chiamata una funzione, il codice effettivo della funzione verrà copiato nella posizione chiamante, invece di essere eseguito saltando al corpo della funzione come una normale funzione.
linline_ref: una funzione che utilizza il modificatore inline_ref ha il codice memorizzato in una cella separata. Ogni volta che viene chiamata una funzione, TVM esegue il codice memorizzato nella cella tramite il comando CALLREF invece di inserire il codice della funzione nella posizione della chiamata.
Pertanto, il modificatore inline è adatto per funzioni semplici, riducendo il sovraccarico delle chiamate di funzione, ma può portare alla duplicazione del codice del contratto, mentre il modificatore inline_ref è adatto per funzioni più complesse o con chiamate multiple, che migliora l'efficienza memorizzando il codice della funzione in un file; cella separata. Efficienza ed evitare la duplicazione del codice. Quindi può essere riassunto come segue: quando la funzione è grande o chiamata da più posti, si consiglia di utilizzare inline_ref altrimenti si consiglia di utilizzare inline;
(5) Determinare la catena di lavoro corretta
TON consente la creazione di un massimo di 2^32 catene di lavoro e ciascuna catena di lavoro può essere suddivisa in un massimo di 2^60 shard. In precedenza, esistevano solo 2 catene di lavoro: la catena principale (-1) e la catena di base (0 ). Quando si calcola l'indirizzo di destinazione nel contratto, l'ID della catena a cui appartiene l'indirizzo di destinazione deve essere specificato esplicitamente per garantire che l'indirizzo del portafoglio generato si trovi sulla catena di lavoro corretta. Per evitare di generare indirizzi errati, si consiglia di utilizzare force_chain() per forzare la specifica dell'ID della catena.
(6) Evitare conflitti di codici di errore
Nella progettazione contrattuale, per garantire la standardizzazione ed evitare confusione, la gestione dei codici di errore è molto critica. Per i contratti intelligenti TON, prima di tutto, dovresti assicurarti che ogni codice di errore sia univoco nel contratto ed evitare di definire codici di errore ripetuti nello stesso contratto per evitare confusione di codici di errore e informazioni poco chiare, in secondo luogo, la piattaforma TON o il sistema sottostante lo hanno; Sono definiti alcuni codici di errore standard e i conflitti con questi codici di errore di sistema dovrebbero essere evitati. Ad esempio, l'ID della catena rappresentato dal codice di errore 333 non corrisponde. Pertanto, si consiglia che il codice errore del contratto sia compreso tra 400 e 1000.
(7) Al termine dell'operazione, è necessario memorizzare i dati e chiamare return()
Nei contratti intelligenti TON, l'elaborazione dei messaggi selezionerà una logica diversa in base al codice operativo. Dopo aver completato la logica aziendale corrispondente, è necessario completare due operazioni: in primo luogo, se sono coinvolte modifiche ai dati, è necessario chiamare save_data() per garantire che i dati siano archiviati, altrimenti le modifiche non saranno valide; in secondo luogo, return() deve esserlo chiamato per indicare che l'operazione è completata, altrimenti verrà attivata un'eccezione Throw(0xffff).
() recv_internal(int my_balance, int msg_value, cella in_msg_full, slice in_msg_body) impuro {
int flag = cs~load_uint(4);
se (flag & 1) {
;; ignora tutti i messaggi respinti
ritorno ();
}
fetta sender_address = cs~load_msg_addr();
carica_dati();
int op = in_msg_body~load_op();
se ((op == op::op_1())) {
gestire_op1();
salva_dati();
ritorno ();
}
se ((op == op::op_2())) {
gestire_op2();
salva_dati();
ritorno ();
}
se ((op == op::op_3())) {
gestire_op3();
salva_dati();
ritorno ();
}
lancia(0xffff);
}
Riassumendo, la blockchain di TON sta gradualmente diventando una piattaforma ideale per gli sviluppatori di applicazioni decentralizzate con la sua architettura innovativa e un ambiente di sviluppo flessibile. Tuttavia, poiché i contratti intelligenti svolgono un ruolo sempre più importante nell’ecosistema TON, le questioni legate alla sicurezza dei contratti non possono essere ignorate. Gli sviluppatori dovrebbero avere una profonda conoscenza delle caratteristiche dell'ecosistema TON, seguire rigorosamente le migliori pratiche, rafforzare il processo di controllo della sicurezza e garantire la robustezza e la sicurezza del contratto. Solo in questo modo potremo sfruttare appieno i vantaggi della piattaforma TON, costruire applicazioni decentralizzate più sicure e affidabili e salvaguardare il sano sviluppo dell’intero ecosistema.
Attualmente, l’ecosistema TON si sta sviluppando rapidamente, attirando una grande quantità di fondi e utenti attivi. Tuttavia, i problemi di sicurezza che ne derivano non possono essere ignorati.