Hey Filip,
assert() require()
invariants : a condition that is always true at a particular point.
One step closer to building my crypto.
DOODESVILLE
Hi @gmarcotte,
Good questions!
Have a look at this post, and the ones following it, for some answers and comments about this. They are directly above your post.
Another good question! I would be tempted to say thatās just how Solidity has been designed: mappings for efficient key-to-value look-ups, and arrays for sequential storage and iteration. Mappings in Solidity do not store data sequentially, as I think they do in Maps in JavaScript, and so that is probably one reason why you can iterate over one but not the other. But that still doesnāt get to heart of why they have been designed to work that way. Maybe, as you suggest, there are some specific blockchain reasons, such as gas costsā¦although, this is just a guess. Iām tagging a few colleagues, to see if they have anything additional they can add about this @dan-i, @Taha, @thecil, @gabba, @Alko89
Hello filip, can you take a look at the last line on this coding. I keep getting a red marker in the console when it compiles and I cant figure out why.
Also, I am not really āgettingā this.
Will it ever sink in?
DOODESVILLE
pragma solidity 0.5.12;
contract HelloWorldAgain{
struct Person {
uint id;
string name;
uint age;
uint height;
bool senior;
}
address public owner;
constructor() public{
owner = msg.sender;
}
mapping(address => Person) private people;
address[] private creators;
function createPerson(string memory name, uint age, uint height) public {
require (age < 150, "age needs to be below 150");
//This Creates a Person
Person memory newPerson;
newPerson.name = name;
newPerson.age = age;
newPerson.height = height;
if (age >= 65){
newPerson.senior = true;
}
else{
newPerson.senior = false;
}
insertPerson(newPerson);
creators.push(msg.sender);
assert(
keccak256(
abi.encodePacked(
people[msg.sender].name,
people[msg.sender].age,
people[msg.sender].height,
people[msg.sender].senior
)
)
==
keccak256(
abi.encodePacked(
newPerson.name,
newPerson.age,
newPerson.height,
newPerson.senior
)
)
);
}
function insertPerson(Person memory newPerson) private {
address creator = msg.sender;
people[creator] = newPerson;
}
function getPerson() public view returns(string memory name, uint age, uint height, bool senior){
address creator = msg.sender;
return (people[creator].name, people[creator].age, people[creator].height, people[creator].senior);
}
function deletePerson(address creator) public {
require(msg.sender == owner, "Caller needs to be owner");
delete people[creator];
assert(people[creator].age == 10);
}
function getCreator(uint index) public view returns(address){
require(msg.sender == owner, "Caller needs to be owner");
return [creators]index;
}
Your getCreator
function, the returned value is bad written.
[creators]index
incorrect.
return(creators[index]);
correct way, your creators
variable is an array, array indexed value is called has arrayName[indexPosition]
.
Carlos Z.
Iām still in the beginning of my solidity path but canāt we use a data structure that makes searching easier like kind of a binary tree or is it going too far in matter of memory usage?
P.S. The question is regarding the array with creators example
Hey @RKumovski
Arrays should be used only if they are strictly necessary and keep their iterations low to avoid using gas.
In Solidity the best data structure in this sense are surely mappings.
mapping (key => value ) mappingName
.
Happy learning,
Dani
It seems advanced data structures and algorithms have to be done ⦠off chain ⦠layer 2 ⦠etc, etc.
Although Ethereum began with the concept of adding Turing-complete programmability, itās clear that the ācostā of doing that has not been resolved in the implementation and execution of Ethereum.
There are many, many projects and thoughts about how to improve the situation, but it is a clear and present concern.
I did not understand how you used address creator=msg.sender, what does it mean: msg.sender indicates the current message(the call) of the sender, I did not understand the function of it?
I did not understand the use of owner as a second address as well? which msg.sender used in this case creators.push(msg.sender), msg.sender of owner or of creator
msg.sender
means the caller of the function (an address or account, a user, could be you or other).
the owner
address is used to store the first msg.sender
, the person who deployed the contract, it will be saved has owner
. After that, you can use the owner
variable to specify functions to be only invoked from the same address only.
If you have any more questions, please let us know so we can help you!
Carlos Z.
but if we replace owner by creator, nothing changes? and concerning
require(msg.sender == owner, āCaller needs to be ownerā); when I remove it, everything remains the same.
In the case of require: as I understood, it means that we will get person only if we input the ownerās address but in all cases with require or not, when we enter another address in the smart contract, it will not give a result because it doesnāt know it and I tried it and nothing changes when I remove require.
Is it possible to replace msg.sender with creator or owner in this case, if it doesnāt work, why not?
assert(
keccak256(
abi.encodePacked(
people[msg.sender].name,
people[msg.sender].age,
people[msg.sender].height,
people[msg.sender].senior
)
)
==
keccak256(
abi.encodePacked(
newPerson.name,
newPerson.age,
newPerson.height,
newPerson.senior
)
)
);
This discussion was very useful. Reading through this helped me understand deeply about how solidity is working under the hood. Thanks @jon_m & @gabba for the deep dive.
it is possible to change the msg.sender with any other variable you want.
Just think as msg.sender
has the person who invoke or call a function.
Instead of writing the address of the account owner like people[0x2bf89234...]
you will use the owner
variable just to save that address and then use it in other ways for your code. Creators variable is the same but for other use cases, is not the variable name what define to do something or not, is based on what you want to achieve.
If you have any more questions, please let us know so we can help you!
Carlos Z.
Hello Out there. I get an error in this. Can someone helpme out?
pragma solidity 0.5.12;
contract HelloWorld{
struct Person {
string name;
uint age;
uint height;
bool senior;
}
mapping(address => Person) private people;
function createPerson(string memory name, uint age, uint height)
Person memory newPerson;
newPerson.name = name;
newPerson.age = age;
newPerson.height = height;
if(age > 65){
newPerson.senior = true;
}
else{
newPerson.senior = false;
}
insertPerson(Person);
}
function insertPerson(Person memory newPerson) private {
address creator = msg.sender;
people[creator] = newPerson;
}
function getPerson() public view returns(string memory name, uint age; uint height, bool senior){
address creator = msg.sender;
return (people[creator]. name, people[creator].age, people[creator].height,people[creator].senior)
}
Hey @Ako1, hope you are great.
Your code have a lot of errors:
Your function createPerson
must contain a visibility level (public), at the end, you are sending Person
on the insertPerson
function, should send newPerson
instead, because is the variable that you are using in order to set the new Person parameters.
At the end of all the code, you will need an extra }
which belongs to the contract body.
Carlos Z
Hi!
Iām confused with why Filip is using the creators array to delete a user.
I understand that it is not possible to loop through a mapping or to query the number of elements in a mapping. Therefore, if we want to keep track of the addresses in our mapping, we need to store them also in an array.
But how does this help with deleting a person?
If we want to delete Bob, we need Bobās address because thatās the key of the people mapping.
-
Maybe Bob sends a delete request to the contract owner. In this case we retrieve Bobās address from msg.sender and thereās no need for an array with addresses.
-
Or maybe Bob asks to be deleted without providing his address. In that case we need an extra data structure that links Bobās name to his address. Again there doesnāt seem to be a need for an array of just addresses.
Filip uses an index. He doesnāt explain where this index is coming from, but it would have to come from a data structure that links Bob to his index in the creators array. But why would we link Bobās name to his index in the creators array instead of linking it to his address? Again the creators array seems unnecessary. What we really need is a link between Bobās name and his address.
So why did we need the creators array? Filip says it is a common construction in contracts, but I donāt see the point (at least not in this case). What am I missing?
Thanks!
I have tried, but the error still keeps popping up.
Hi @Ako1,
function createPerson has no curly brackets at the end of the header.
In function getPerson you have a semi-colon after āuint ageā, it should be a comma.
The return statement of function getPerson has no semi-colon at the end.
With those additional changes your code worked for me.
Hope this helps.
It seems like the code was modified ābetween videosā. It is tricky for a novis to follow.
Please reduce the size of the code window so the complete lines are visible, this would also be very helpful and enhance understanding.
When I look at the code in github, I have to check many files as the names does not correspond to the name if the lecture. Please rename the files for a more easy-to-follow experience. It is nice to follow the developing of the code, not to discover I missed some adjustments.
Hi @filip @thecil & team.
In the Solidity Error Handling section, when @filip explained about assert
, he hashed Person
object using the following technique.
keccak256(abi.encodePacked(...))
I have question regarding abi.encode(...)
vs abi.encodePacked(...)
I did further research & I just want to share my findings. Please let me know if my findings are correct ?
abi.encodePacked(...)
works simpler than abi.encode(...)
. Therefore, in some case can save consumption of gas. Example,
-
abi.encode("test")
will give an output of the following32 bytes of 3 words
, which are:
0x0000000000000000000000000000000000000000000000000000000000000020
0x0000000000000000000000000000000000000000000000000000000000000004
0x7465737400000000000000000000000000000000000000000000000000000000
which (from top to bottom) refer to:
1st line⦠position of the parameter (ātestā) padded to 32-bytes
2nd line⦠length of the parameter padded to 32-bytes, which is 4
3rd line⦠the actual data(ātestā) UTF-8 encoded padded to 32-bytes -
abi.encodePacked("test")
will give an output of the following unpadded utf-8 encoding, which is:
0x74657374
abi.encodePacked(...)
is simplier. But, structs as well as nested arrays are not supported. Therefore, in this case, we might need to use abi.encode(...)
.
Please let me know if my understanding above is correct and add in if I miss anything ?
Reference (I use the following online converter):
https://adibas03.github.io/online-ethereum-abi-encoder-decoder/#/encode
https://mothereff.in/utf-8
Regards,
Willys