Welcome to the thread about Error handling in Ethereum. Here you can discuss everything about this chapter.
Using DogContract.sol
We write the Kettle Code with the Breed included, and the internal settings and error handling.
pragma solidity^0.4.0; import './DogContract.sol'; contract Kennel is DogContract { function transferDog(address _newOwner) private{ address owner = msg.sender; //Soft. Throws error if sender is new owner. Soft. require(owner != _newOwner); uint dogId = ownerToDog[owner]; delete(ownerToDog[owner]); ownerToDog[_newOwner] = dogId; //Strict. Will not happen. assert(ownerToDog[owner] == 0); } function addKennelDog(string _name, uint _age, string _breed) { addDog(_name, _age, _breed); } }
If an assert is false, what happens to the instructions already executed? Eg. if both sender and receiver now own the same dog and assert causes the contract to exit - will the owner revert back to sender only or are we stuck with the âdouble spendâ problem??
If you use assert, the changes to state variables will be reverted. So all data within the contract would be in the same state as it was before the execution.
Got it, thanks!!!
Hi, where are the exercises, exactly ? You only show the solutions. ^^
How to customize the error message?
By mistake I thought the quizzes were called exercises. So when I say exercises in the video and mean the quizzes most of the time.
There is no way to customize an error message with the require or assert functions. The best option is to use an event.
So! for handle the error what is it a better practice to use ârequierâ or âassertâ?
It depends on the situation. Here is a good explanation about the difference between the two.
What is revert() ? Could you elaborate more?
@filip Are there ways of exiting gracefully using assert and require? Or is it desirable for them to throw what looks like fatal errors?
I rewrote the code in the getDog() function to take into account owners who do not have dog IDs mapped to them using a simple if statement so not to throw an error and return a string:
function getDog() view public returns (string) { address owner = msg.sender; uint id = ownerOfDog[owner]; if (id == 0) { return "No dog found"; } return dogs[id-1].name; }
I originally rewrote the addDog() function to store the actual index of the array in the mapping, thinking it would make things simpler later on as we are only really interested in index, not the return value:
ownerOfDog[msg.sender] = dogs.push(Dog(_name, _age)) - 1;
I later realised this is not a good idea, as I discovered all keys in a mapping will hold a value of zero if they are not assigned to anything. This means the first dog owner will have the same value as any other owner who does not yet have a dog!
My question at this point is, is it possible (and if so, is it desirable) to use assert() or require() to check if an owner has a dog, and if not exit gracefully by returning a string rather than throwing an error?
You can also test the assert this way:
pragma solidity ^0.4.0;
import â./Arrays.solâ;
contract Kennel is dogContract {
function transferDog (address _newOwner){
address owner = msg.sender;
//require (owner != _newOwner);
uint dogID = ownerToDog[owner];
// delete ownerToDog[owner];
ownerToDog[_newOwner] = dogID;
assert(ownerToDog[owner] == 0);
}
function addKennelDog (string _name, uint _age){
addDog(_name,_age);
}
}
If you do not carry out the DELETE, then both new and old owners will have the dog, and therefore the assertion will fail. This is probably the thing we would be most likely to forget.
of course you donât have to comment out the requirement as well to test this.
This is a good improvement to the contract! I think you will learn a lot by doing it.
I think the best way to do it would be to change to mapping so it stores the Struct instead of the uint id. Since all values in a mapping will be initialized to its âzero valueâ, itâs a bit trickier to check whether the owner has a dog when we are using a custom Struct as the value.
The best thing to do to simplify and clarify the if statement would be to add an âexistsâ property to the dog struct, and set it to true every time you add a dog to the mapping. Then in the if statement instead of checking if (id == 0) you would check if (dog.exists == false).
Let me know if my explanation was clear. Otherwise I can explain further. Good job dude!
Hi Filip,
Are there any libraries for Solidity that already include typical functions or code pieces so we donât need to reinvent the wheel and do everything from scratch? â Thanks!
Yes, openzeppelin is a great one. I cover it in this video on my youtube channel: https://www.youtube.com/watch?v=TAbcztHglT8
When I update the Programming Course early next year it will include openzeppelin as well.
Thank you so much, Filip!
Quiz, second question.
I chose only the first option, that assert is needed to validate inputs since my thought is that the second option would be too restrictive and thus not valid. Seems that both are true ?
I thought this was not very clear. I also was not aware that more options could be selected. (maybe to mention that somewhere at the beginning of the course? Or did I miss it somewhere?)