Debug delle Transazioni

(si veda anche la pagina di accompagnamento di questa: il tour del Debugger)

Esistono due modi per avviare una sessione di debug, ognuno dei quali corrisponde a un caso d’uso diverso.

  • Caso d’uso 1: per eseguire il debug di una transazione effettuata in Remix, fare clic sul pulsante Debug nel registro delle transazioni nel Terminale di Remix.

  • Caso d’uso 2: per il debug di una transazione in cui si dispone di un”hash di transazione da un contratto verificato o in cui si dispone dell’hash di transazione e del codice sorgente compilato con le stesse impostazioni di compilazione del contratto distribuito.

Avviare il debug dal registro delle transazioni nel Terminale

Cominciamo con un contratto di base (o sostituisci il contratto qui sotto con il tuo)

pragma solidity >=0.5.1 <0.6.0;
contract Donation {
    address owner;
    event fundMoved(address _to, uint _amount);
    modifier onlyowner { if (msg.sender == owner) _; }
    address[] _giver;
    uint[] _values;

    constructor() public {
        owner = msg.sender;
    }

    function donate() public payable {
        addGiver(msg.value);
    }

    function moveFund(address payable _to, uint _amount) onlyowner  public {
        uint balance = address(this).balance;
        uint amount = _amount;
        if (amount <= balance) {
            if (_to.send(balance)) {
                emit fundMoved(_to, amount);    
            } else {
               revert();
            }
        } else {
            revert();
        }
    }

    function addGiver(uint _amount) internal {
        _giver.push(msg.sender);
        _values.push(_amount);
    }
}
  • Crea un nuovo file in Remix e copiaci dentro il codice di cui sopra.

  • Compila il codice.

  • Vai al modulo Esegui & Distribuisci.

Per lo scopo di questa esercitazione, eseguiremo la Remix VM.

  • Distribuisci il contratto:

Fare clic sul pulsante Distribuisci

Si vedrà l’istanza distribuita (alias udapp).

Quindi aprilo (facendo clic sul cursore).

Invocheremo la funzione Dona e invieremo 2 Ether.

Per farlo: nella casella di inserimento del valore immettere 2 e selezionare Ether come unità (NON LASCIARE L’UNITÀ DI DEFAULT come gwei o la modifica sarà difficile da rilevare).

Quindi clicca sul pulsante Dona.

Questo invierà l’Ether alla funzione.

Because we are using the Remix VM, everything happens almost instantly. (If we had been using Injected Web 3, then we would have needed to approve the transaction, pay for gas and wait for the transaction to get mined.)

Remix visualizza le informazioni relative a ciascun risultato della transazione nel terminale.

Controlla nel terminale dove è registrata la transazione appena effettuata.

Clicca sul pulsante di debug.

Ma prima di passare allo strumento di debug vero e proprio, la prossima sezione mostra come avviare una sessione di debug direttamente dal Debugger.

Avviare il debug dal Debugger

Fai clic sull’icona del bug nel pannello delle icone per accedere al debugger nel pannello laterale.

Se non si vede l’icona del bug, vai nel gestore dei plugin e attiva il debugger.

È possibile avviare una sessione di debug fornendo un hash di transazione.

Per trovare un hash di transazione:

  1. Vai a una transazione nel terminale.

  2. Fai clic su una riga con una transazione - per espandere il registro.

  3. L’hash di transazione è lì: copialo.

Quindi fai clic nel debugger per incollare l’hash e premi il pulsante Avvia il debug.

Utilizzare il debugger

Il debugger consente di visualizzare informazioni dettagliate sull’esecuzione della transazione. Esso utilizza l’editor per visualizzare la posizione nel codice sorgente in cui si trova l’esecuzione attuale.

La parte di navigazione contiene un cursore e dei pulsanti che possono essere utilizzati per procedere con l’esecuzione della transazione.

Spiegazione delle funzionalità del pulsante Debugger

  1. Passa Sopra Indietro Ritorna al passo precedente, ma ignora/passa sopra le chiamate di funzione: il debugger NON inserirà una funzione

  2. Passa Indietro Ritorna al passo precedente. Non ignora le chiamate della funzione: il debugger INSERIRÀ qualunque funzione lungo il percorso

  3. Passa Dentro Avanza al prossimo passo. Non ignora le chiamate di funzione: il debugger INSERIRÀ qualsiasi funzione lungo il percorso

  4. Passa Sopra Avanti Avanzerà verso il prossimo passo, ma ignora/passa sopra le chiamate di funzione: il debugger NON inserirà una funzione

  5. Salta al punto di interruzione precedente Invia il debugger all’ultimo punto di interruzione visitato. Si noti che i punti di interruzione possono essere impostati facendo clic sul numero di riga nel codice sorgente

  6. Salta fuori Invia il debugger alla fine della funzione

  7. Salta al punto di interruzione successivo Invia il debugger al punto di interruzione successivo

11 pannelli forniscono informazioni dettagliate sull’esecuzione:

Istruzioni

Il pannello Istruzioni visualizza il bytecode del contratto in esecuzione, con il passaggio attuale evidenziato.

Nota importante: quando questo pannello è nascosto, il cursore avrà una granularità più grossolana e si fermerà solo ai confini delle espressioni, anche se queste sono compilate in più istruzioni EVM. Quando il pannello è visualizzato, sarà possibile passare sopra ogni istruzione, anche quelle che fanno riferimento alla stessa espressione.

Solidity Locals

Il pannello «Solidity Locals» visualizza le variabili locali associate al contesto attuale.

Stato di Solidity

Il pannello Stato di Solidity visualizza le variabili di stato del contratto in esecuzione.

Pannelli di basso livello

Questi pannelli mostrano informazioni di basso livello sull’esecuzione:

  • Stack

  • Modifiche dell’Archiviazione

  • Memoria

  • Dati d’Invocazione

  • Pila d’Invocazione

  • Valore Restituito (solo se il passaggio corrente è un codice operativo RETURN)

  • Full Storages Changes (only at the end of the execution & it displays all the storage changes)

Transazione Stornata

Una transazione può essere stornata (a causa di un’eccezione carburante esaurito, di un’istruzione revert di Solidity o di un’eccezione di basso livello).

È importante essere consapevoli dell’eccezione e individuare la posizione dell’eccezione nel codice sorgente.

Remix ti avviserà quando l’esecuzione genera un’eccezione. Il pulsante di avvertimento salta all’ultimo codice operativo prima che si verificasse l’eccezione.

Punti di interruzione

Gli ultimi due pulsanti dell’area di navigazione servono per tornare al punto di interruzione precedente o per passare al punto di interruzione successivo.

I punti di interruzione possono essere aggiunti e rimossi facendo clic sul numero di riga nell’Editor**.

Quando si utilizza una sessione di debug con punti d’interruzione, l’esecuzione salta al primo punto d’interruzione incontrato.

Nota importante: se si aggiunge un punto d’interruzione a una riga che dichiara una variabile, questo potrebbe essere attivato due volte: una prima volta per inizializzare la variabile a zero e una seconda volta per assegnare il valore effettivo.

Ecco un esempio di questo problema. Se stai facendo debug del seguente contratto:

pragma solidity >=0.5.1 <0.6.0;

contract ctr {
    function hid() public {
        uint p = 45;
        uint m;
        m = 89;
        uint l = 34;
    }
}

E punti d’interruzione sono impostati per le linee

uint p = 45;

m = 89;

uint l = 34;

allora, facendo clic sul pulsante Salta al prossimo punto di interruzione, si fermerà alle righe seguenti nell’ordine indicato:

uint p = 45; (dichiarazione di p)

uint l = 34; (dichiarazione di l)

uint p = 45; (45 assegnato a p)

m = 89; (89 assegnato a m)

uint l = 34; (34 assegnato a l)