2016-04-13 8 views
6

Ich habe eine Funktion auf einen Soliditätsvertrag, der einen Wurf macht. Z.B.Was ist das Muster für die Handhabung werfen auf einen Soliditätsvertrag in Tests

function do(x,y) { 
     if (msg.sender != owner) 
      throw; 
     // ... 
    } 

In der Trüffel Umgebung habe ich einen etwas js Test wie:

//.... part of a promise chain 
     .then(
      function (_bool0) { 
       assert.isTrue(_bool0,"whoops - should be true"); 
       return contract.do("okdoke" , {from: accounts[1]}); 
      }).then(
      function (tx_id) { 
       //.. 
       done(); 
      } 
    // ... 

Die Rückkehr contract.do() bewirkt, dass die Bedingung, die in dem Wurf führt. Welche produziert die folgenden in der Trüffel Test Ausgang für diesen Test:

Error: VM Exception while executing transaction: invalid JUMP 

Was ist das Idiom einen Wurf aus einem Vertrag Funktion in einem Test, wie dies für den Umgang mit? Der Wurf ist das richtige Verhalten.

+0

Caveat für niemanden zu sehen nach dem „prüfen, ob alle Gas verwendet wird“ Ansatz: Das wird sich ändern in der Zukunft, da throw zugunsten von "revert()" veraltet ist. Revert() verbraucht NICHT das gesamte Gas, sondern nur das Gas, das bis zu dem Punkt verbraucht wurde, an dem revert() aufgerufen wurde. – TripleSpeeder

+0

Dieses Problem verursacht immer noch Probleme, besonders da ich revert() verwende. Ich konnte auf TestRPC nach Fehlern suchen, aber jetzt, da ich im Rinkeby-Test bin, kehren Netzwerktransaktionen zurück, als wären sie erfolgreich. irgendwelche Updates auf deiner Seite? – okwme

Antwort

3

Die "richtigste" Lösung für dieses Problem, das ich in der Lage gewesen bin, zu überprüfen ist das gesamte gesendete Gas wurde ausgegeben, was auf einem Wurf auftritt, aber es gibt eine zusätzliche Falte, um die Lösung zu machen arbeite an beiden TestRPC (was ich vermute, dass du benutzt wirst, da der eigentliche Fehler geworfen wurde) und Geth. Wenn in Geth ein Wurf stattfindet, wird immer noch eine Transaktion erstellt, die das gesamte Gas ausgibt, aber es treten keine Zustandsänderungen auf. TestRPC löst tatsächlich den Fehler aus, der für Debugging-Zwecke nützlich ist.

//Somewhere where global functions can be defined 
    function checkAllGasSpent(gasAmount, gasPrice, account, prevBalance){ 
     var newBalance = web3.eth.getBalance(account); 
     assert.equal(prevBalance.minus(newBalance).toNumber(), gasAmount*gasPrice, 'Incorrect amount of gas used'); 
    } 

    function ifUsingTestRPC(){ 
     return; 
    } 

    //Some default values for gas 
    var gasAmount = 3000000; 
    var gasPrice = 20000000000; 

    .... 

    //Back in your actual test 
    it('should fail ', function (done) { 
     var prevBalance; 

    .... 

    .then(function (_bool0) { 
     assert.isTrue(_bool0,"whoops - should be true"); 
     prevBalance = web3.eth.getBalance(accounts[1]); 
     return contract.do("okdoke" , {from: accounts[1], gasPrice:gasPrice, gas:gasAmount }); 
     }) 
    .catch(ifUsingTestRPC) 
    .then(function(){ 
     checkAllGasSpent(gasAmount, gasPrice, accounts[1], prevBalance); 
    }) 
    .then(done) 
    .catch(done); 

Ich würde fröhlich eine einfachere Lösung implementieren, wenn eine andere erscheint.

Hinweis: Wenn Sie das gesamte Gas mit einer Transaktion ausgeben, die aus Versehen gültig ist, wird dies nicht erkannt - es wird davon ausgegangen, dass das Gas durch einen Wurf in der VM verbraucht wurde.

+0

danke, dass du dir die Zeit genommen hast, darüber nachzudenken, aber ich habe mehr danach gesucht, wie ich mit dem Wurf umgehen soll. Die VM Exception scheint nur alles zu zerstören, aber ich hoffte auf mehr von einer Versuchs-/Fang-Technik, um die Kontrolle zu behalten. Anstatt alles auseinander zu fallen. – Interition

0

Nur, damit jeder weiß, ich auf diesem Problem auch gekommen und hat die folgend benutzen:

function getTransactionError(func) { 
    return Promise.resolve().then(func) 
    .then(function(txid) { 
     var tx = web3.eth.getTransaction(txid); 
     var txr = web3.eth.getTransactionReceipt(txid); 
     if (txr.gasUsed === tx.gas) throw new Error("all gas used"); 
    }) 
    .catch(function(err) { 
     return err; 
    }); 
} 

Auf Geth es die Transaktions-ID verwendet verfügbares Gas und verwendetes Gas zu erhalten und gibt einen Fehler in Fall wurde alles Gas verwendet. Bei testrpc fängt es einfach die geworfene Ausnahme ab und gibt sie zurück. Ich verwende in einem Test es wie folgt:

return getTransactionError(function() { 
    return contract.doSomething(); 
}).then(function(err) { 
    assert.isDefined(err, "transaction should have thrown"); 
}); 

Natürlich kann man auch den Fang auslassen, wobei in diesem Fall das Versprechen wird einfach mit einem Fehler fehlschlagen, wenn es geworfen wurde.

2

Das zeppelin Projekt als fantastischer Weg, genau das zu tun:

it("should fail to withdraw", async() => { 
    try { 
     await receiver.withdrawToken(0x0); 
     assert.fail('should have thrown before'); 
    } catch(error) { 
     assertJump(error); 
    } 
    }); 

function assertJump(error) { 
    assert.isAbove(error.message.search('invalid opcode'), -1, 'Invalid opcode error must be returned'); 
} 

https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/test/Ownable.js Um ein vollständiges Beispiel

Verwandte Themen