Solidity Error Handling Assignment


#21

You could also check so that the name is an empty string. But it would accomplish the same thing.


#22

This is my function:

function addDog(string memory _name, uint _age) public{
    require(ownerToDog[msg.sender].age == 0, "Owner already has a dog");
    require(_age > 0, "Dog's age should be higher than 0");
    Dog memory currentDog = Dog(_name, _age);
    ownerToDog[msg.sender] = currentDog;
}

Is there a way to simplify it?


#23

Looks good. You could simplify it a little bit by doing this.

require(_age > 0 && ownerToDog[msg.sender].age==0)

But then you wouldn’t be able to have 2 different error messages.


#24

Thanks Filip, I though so


#25

pragma solidity 0.5.1;

contract DogContract{
struct Dog {
string name;
uint age;

}

mapping(address => Dog) ownerToDog;

function addDog(string memory _name, uint _age) public {

   require(ownerToDog[msg.sender].age == 0); 
   Dog memory currentDog = Dog(_name, _age);
   ownerToDog[msg.sender] = currentDog;
   assert(currentDog.age == _age);

}
function getDog() public view returns (string memory) {
address owner = msg.sender;
return ownerToDog[owner].name;

}

}


#26

Hello Fillip,
I have to admit I had to rerun the “Mapping Assignment” detecting the flaw and “Solution + Control Flow” several time in order to figure out where do I begin finding what this error (flaw) may be. LOL, now I “think” I understand that it’s the correct method of writing the contract.
I understand the logic correcting the flaw is to prevent adding more than one dog which will cause the mapping will be overwritten, as you stated, and clearly demonstrated that “one key can only have one value”.
So here are my questions:

  1. This is not an instance of code errors but to ensure nothing is overwritten on a current contract?
  2. Can you please describe a scenario, aside from the “Dog” example that this would occur in a real scenario? This would help me confirm what I am learning in this exercise.

#27

Sorry, forgot to add the “Error Handling Assignment Solution”…
pragma solidity 0.5.1;

contract DogContract{
struct Dog {
string name;
uint age;
}

mapping(address => Dog) ownerToDog;

function addDog(string memory _name, uint _age) public{
   require(ownerToDog[msg.sender].age ==0);/*The dog has already been registered*/
   
   Dog memory currentDog = Dog(_name, _age);
   ownerToDog[msg.sender] = currentDog;
 
}
function getDog() public view returns(string memory){
    address owner= msg.sender;
    return ownerToDog[owner].name;
    
}

}


#28
  1. Exactly, not code errors. But quite stupid code. So even though there is nothing wrong in the code per say, it might not behave like people would think.

  2. It’s hard to come up with a real world example. But let’s say instead of dogs you saved users of your dapp. And you forgot to make a check to see if a user already exists with that same address. Then you might overwrite the data of the already registered user.


#29

Hi @filip
Here is my answer, i tried a different example to test assert and require after testing the basic one and
i struggle to find a simple way to compare string.

pragma solidity 0.5.1;

contract DogContract{
    struct Dog {
        string name;
        uint age;
    }
    
    mapping(address => Dog) ownerToDog;
    
    function compare(string memory _a, string memory _b) private pure returns (int){
        bytes memory a = bytes(_a);
        bytes memory b = bytes(_b);
        uint minLength = a.length;
        if (b.length < minLength) minLength = b.length;
        for (uint i = 0; i < minLength; i ++)
            if (a[i] != b[i])
                return 1;
        if (a.length != b.length)
            return 1;
        else
            return 0;
    }
    
    function addDog(string memory _name, uint _age) public{
        require(bytes(_name).length != 0
                && _age != 0,
                "name or age couldn't be null");
        require(compare(ownerToDog[msg.sender].name, _name) == 1
                ,"Owner already have a dog with the same name, try an other one !");
        
        Dog memory currentDog = Dog(_name,_age);
        ownerToDog[msg.sender] = currentDog;
        assert(compare(ownerToDog[msg.sender].name, _name) == 0);
    }

    function getDogName() public view returns (string  memory){
        return ownerToDog[msg.sender].name;
    }
    
    function getDogAge() public view returns (uint){
        return ownerToDog[msg.sender].age;
    }
}

I found the StringUtils library but i just implement a light version of it as it cost me less gas.
I found an example on Stackoverflow where a guy is using keccak256() to compare two string but in my example an error occur:

    keccak256(ownerToDog[msg.sender].name);
              ^-------------------------^

TypeError: Invalid type for argument in function call. Invalid implicit conversion from string storage ref to bytes memory requested. This function requires a single bytes argument. Use abi.encodePacked(…) to obtain the pre-0.5.0 behaviour or abi.encode(…) to use ABI encoding.

Is keccak256 only works for static variable ? Is this function hash a string before saving it on the blockchain ? Can we consider it as post processing operation ? Because it works fine when comparing two string.
require(keccak256(“foo”) != keccak256(“bar”))

hashing a string use less gas than comparing 2 strings byte by byte, do you have an other solution to compare string with an efficient way ?
Sorry if my Question is a bit out of the scope :slight_smile:

thx @Capaburro for the notification on require :wink: it helps to debug


#30

Hey @tjourneyinge
I tried a sample with a name comparison, but i am not sure it s the most efficient way, let me know what do you think about it :wink:


#31

pragma solidity 0.5.1;

contract DogContract{

struct Dog { 

    string name;

    uint age;

}

mapping(address => Dog) ownerToDog;

function addDog(string memory _name, uint _age) public {
/* checking that dog not yet assigned*/
require(ownerToDog[msg.sender].age == 0;

Dog memory currentDog = Dog(_name, _age);

OwnerToDog[msg.sender] = currentDog;

}

function getDog() public view returns ( string memory ) {

address owner = msg.sender;

return  ownerToDog[owner].name;

}


#32

mapping(address => dog) ownerdog;
function addperson(string memory _name, uint _age) public {
require(ownerdog[msg.sender].age == 0);
dog memory currentperson = dog(_name, _age);
ownerdog[msg.sender] = currentperson;
}

function getperson() public view returns(string memory){
    address owner = msg.sender;
    return ownerdog[owner].name;
}

#33
pragma solidity 0.5.1;
contract DogContract{
    struct Dog {
        string name;
        uint age;
    }

    mapping(address => Dog) ownerToDog;
    
    function addDog(string memory _name, uint _age) public {
        require(ownerToDog[msg.sender].age == 0); //check if address has a dog assigned already
        Dog memory currentDog = Dog(_name, _age); //create the dog
        ownerToDog[msg.sender] = currentDog;//assign it to the address

        assert(ownerToDog[msg.sender].age == _age);//last safeguard, if something went VERY wrong, fall back
    }

    function getDog() public view returns (string memory) {
        address owner = msg.sender;
        return ownerToDog[owner].name;
    }
}

I then tried to add two dogs to the same address and got an error back as expected.


#34

pragma solidity 0.5.1;

contract DogContract{

struct Dog {
    string name;
    uint age;
}

mapping(address => Dog) ownerToDog;

function addDog(string memory _name, uint _age) public {
 require(ownerToDog[msg.sender].age == 0, "No dog age specified, so sender not authourised.");

    Dog memory currentDog = Dog(_name, _age);
    ownerToDog[msg.sender] = currentDog;
}

function getDog() public view returns (string memory) {
    address owner = msg.sender;
    return ownerToDog[owner].name;
}

}