I have a function on a Solidity contract that does a throw. Eg.
function do(x,y) {
if ( msg.sender != owner )
throw;
// ...
}
In the Truffle environment I have a test js something like:
//.... 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();
}
// ...
The return contract.do() causes the condition that results in the throw. Which produces the following in the Truffle test output for this test:
Error: VM Exception while executing transaction: invalid JUMP
What is the idiom for handling a throw from a contract function in a test like this? The throw is the correct behavior.
The zeppelin project as an awesome way to do just that:
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 To see a full example
The 'most correct' solution to this problem I've been able to come up with is checking all the gas sent has been spent, which is what occurs on a throw, but there is an additional wrinkle to make the solution work on both TestRPC (which I am guessing you are using, given the actual error being thrown) and Geth. When a throw occurs in Geth, a transaction is still created, spending all the gas, but no state changes occur. TestRPC actually throws the error, which is useful for debugging purposes.
//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);
I'd cheerfully implement a more straightforward solution if another appears, though.
NB If you spend all of gas the with a transaction that is accidentally valid, this won't spot that - it will assume that the gas was spent due a throw inside the VM.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With