Nel mio ultimo articolo avevo promesso di parlarvi di come sia possibile permettere agli utenti che non possiedono ETH di interagire con la blockchain pagandone le fee con asset che non fossero lo stesso ETH. Di come fosse possibile farlo anche se i citati asset non dovessero implementare metodi di trasferimento che supportino le meta transazioni.


Al contrario sorvolerò sull’argomento trattandolo in maniera molto leggera e punterò la vostra attenzione sullo smart contract che è emerso lavorando a questa feature.

Il modo più lineare per permettere ai vostri utenti di pagare le blockchain fee con un asset ERC20 standard è quello di proporre a questi ultimi un sistema che:

  • Esponga la lista di ERC20 tokens che accetterete come metodo di pagamento;
  • Accetti in ingresso transaction object che descriva l’intenzione del vostro utente che non ha degli ETH;
  • Stimi il costo in GAS e conseguentemente di ETH e conseguentemente dell’asset ERC20 scelto dell’intenzione del vostro utente;
  • Prepari un payload che descriva, oltre a una serie di condizioni, quanti ETH il vostro utente andrà a ricevere in cambio degli ERC20 precedente stimati;
  • Prepari il payload completo della transazione originale che l’utente intendeva broadcastare;
  • Mandi i suddetti payload e li riceva signati dal vostro utente;
  • Controlli le eventuali pre-condizioni e broadcasti la transazione relativa allo scambia di ETH e degli ERC20 precedentemente stimati;
  • Broadcasti la transazione originale che l’utente intendeva broadcastare.

Al termine del flusso, riassumendo, l’utente avrà acconsentito:

  • Alla stima in ERC20 della propria transazione;
  • Di ricevere degli ETH in cambio di ERC20 che verranno spesi in sua vece.

Si… sono ben consapevole che il flusso descritto 

sia pieno di problematiche e superfici di attacco,

ma sono convinto che ciascuna di esse possa essere affrontata. Questo è il motivo per cui vorrei continuare a concentrare la vostra attenzione sullo smart contract di cui parlavo all’inizio. A me piace chiamarlo: Atomic Proxy Smart Contract.

Atomic Proxy Smart Contract

Probabilmente vi starete chiedendo perché Atomic e perché Proxy.

Il motivo è che lo SC in questione

è in grado di gestire una sequenza di transazioni ben definite 

per conto dei loro signatari

L’idea è quella di avere uno smart contract che utilizzi il concetto delle meta-transazioni e prenda come input una catena di transazioni destrutturata.

La catena interpreta il ruolo di un “accordo” tra due o più parti.

Se una delle transazioni che compone la catena dovesse fallire, la catena verrebbe rotta con conseguente decadimento dell’accordo.

I destinatari di queste transazioni devo essere degli smart contract a loro volta e quest’ultimo sarà in grado di verificare il signatario della propria transazione e reagire di conseguenza.

L’unico requisito è che la funzione invocata dalla transazione della catena in oggetto accetti come primo parametro della sua funzione il signatario della transazione stessa.

L’Entry Point

La funzione forward è l’entry point dell’Atomic Proxy smart contract:

Come si può notare gli input della funzione sono i componenti della catena di transazioni.

Per ogni transazione possiamo facilmente identificare la lista dei destinatari, i recipients, e la lista contenente i campi value, i txsValueField.

| Ok, ma cosa ne è del campo data di ciascuna transazione?

Dal momento che ciascun campo data è rappresentato da un esadecimale di lunghezza variabile, quest’ultimo deve essere trattato come un dato di tipo bytes.

Sfortunatamente al momento non è possibile passare come input di una funzione pubblica un array di bytes. Per risolvere questo inconveniente sono stati concatenati tutti i data field della catena di transazioni in un unico parametro di tipo bytes (packedTxsDataField) accompagnato da un array di interi (txsDataSizes) che definisce la lunghezza di ciascun data field.

| Ok, hai il to, value e il data. Cosa ne è dei from?

Come accennato la catena di transazioni rappresenta un accordo tra più parti.

Ogni parte coinvolta deve signare il payload che rappresenta l’intero accordo e ciascun data field deve avere come primo parametro il signatario della transazione.

In questo modo dopo aver spacchettato le packedSignature e i packedTxsDataField sarà possibile verificare che ciascuna transazione abbia il proprio signatario. 

| A cosa servono i campi rimasti?

L’expiration e il salt forniscono alla catena di transazioni un ciclo di vita ben definito.

Il flusso di esecuzione

In breve quello che stiamo facendo è:

  • Creare un array di data field partendo da dei dati incapsulati;
  • Creare un array di signatari partendo da dei dati incapsulati;
  • Fare forwarding di una catena di transazione che rappresenta un accordo.

L’expiration è una delle condizioni più importanti da verificare. Se la catena rappresenta una serie di transazioni che spostano del valore, si vorrà evitare il più possibile di essere vittime della fluttuazione del mercato.

Una sfida interessante è stata la ricostruzione dei dati, come i vari data field:

Utilizzando il metodo slice, messo a disposizione della libreria BytesLib.sol, ed estrapolando ciascun elemento data la propria lunghezza dalla iterazione dei packedTxsDataField e txsDataSizes è stato possibile creare un array che contenesse in maniera ben definita ciascun data field.

La stessa procedura è stata adoperata per l’estrazione delle singole signatures dalla loro rappresentazione impacchettata. L’unica differenza è che le signatures hanno una lunghezza fissa di 65 bytes.

Dopo aver controllato di non essere nel pieno di un replay attack utilizzando la funzione getHash, siamo in grado di estrapolare da ciascun data field il signatario della transazione corrente:

Per accertarsi che i signatari estrapolati dai vari data field trovino corrispondenza tra i signatari del payload che rappresenta l’accordo, ovvero la catena di transazioni, è necessario estrapolare da ciascuna signature il signatario corrispondente.

Questo è possibile farlo grazie al famoso metodo ecrecover.

Come ultimo step non ci resta che prendere in prestito dal progetto uPort project la funzione executeCall per fare il forwarding di ciascuna delle transazioni.

Il contratto destinatario

Nello scenario in cui viene scambiato del valore, possiamo immaginare che uno dei destinatari di una delle transazioni che compongono la catena sia uno smart contract che possiede degli ETH e che possa essere istruito tramite l’invocazione di una funziona al trasferimento degli stessi.

Come si può notare la funzione sendETH accetta come primo parametro il signatario della chiamata a funzione. L’eventuale ETHSender smart contract sarà in grado di verificare il signatario passato come argomento e di proseguire o interrompere l’esecuzione della transazione che ha invocato la suddetta sendETH.

Conseguentemente continuare o interrompere il processamento della catena di transazioni riflettendo così l’adempimento o rottura dell’accordo stipulato tra le parti partecipanti.


Autore: Andrea Francesco Speziale
https://twitter.com/andreafspeziale?s=20

https://medium.com/@andreafspeziale/atomic-ethereum-blockchain-operations-88043eff5f7b

Please follow and like us: