(Part 9) Ethereum Solidity Assembly - Return, Memory, Hexadecimal, Pointers And Memory Addressing(PT 9)
Repository
https://github.com/igormuba/EthereumSolidityClasses/tree/master/class9
What Will I Learn?
- Returning in assembly
- Hexadecimal
- Memory addressing
- Managing multiple data with multiple pointers
- Managing multiple data with a single pointer
Requirements
- Internet connection
- Code editor
- Browser
Difficulty
- Advanced
Tutorial Contents
Before proceeding with this class I recommend to take a look at the previous tutorial, available here
https://steemit.com/utopian-io/@igormuba/part-8-ethereum-solidity-assembly-reducing-costs-and-creation-your-low-level-expression-pt-8
I will, first, show you how can you return from assembly code, as on the previous exercise we have created the uint
variable outside of the assembly code and returned it automatically.
For this, you will need to have a very minimum knowledge about Ethereum memory addressing if you also have previous knowledge of computer architecture, memory management or even some assembly this may be helpful.
Do not worry, the code looks scary and at first, you might not understand, but by using drawings I think I can teach you pretty easily how the concepts of pointers, memory management, bites, bytes and management of the data works behind the curtains
I prefer to first show you the code we will use and shortly after explain what each piece of code does and in the end I will teach a bit more in depth how the memory addressing works and how to keep track of the pointer, we will start on this first code with only one pointer, then we will move to use multiple pointers to store different pieces of data in different physical places of the memory, then we will go back to using one single pointer to store multiple chunks of data!
The return in assembly
pragma solidity ^0.5.0; //version of solidity we will use
contract assemblyReturn{
function storeAccess(uint number) public returns (uint){
assembly{ //prepares to receive assembly
let _pointer:=add(msize(), 1) //starts the pointer in the end
mstore(_pointer, number) //stores the variable on the pointer
return(_pointer, 0x20) //returns from the pointer + 0x20(HEX) bytes
}
}
}
msize
gets the size of the memory and adds one to it, then assigns the result to a variable that is the pointer we will use to keep track of where we are storing the information
mstore(x,y)
store the data y
starting on the position x
of the memory
return(x,y)
returns the data from the point x
until the point x+y
on the memory
0x20
means it is a 256bits group of data in hexadecimal
The tricky part probably for someone that has never touched assembly or do not understand other numeric systems other than decimals, here is a quick intro to hexadecimal, they are used to keep track of how many bytes we are using as distances are measured in bytes, even though uint256 says in its name in bits (256bits)
Hexadecimal
The simple way to convert decimal to hex and hex to dex is to Google, literally, if you google "32 in hex" you will get exactly 0x20
The number you use on your daily life is called decimals, and they go like "1, 2, ...., 8, 9, 10, 11, ....., 19, 20, 21, ..."
Another numeric system you might have heard before is the binary system, here is an introduction to Binary numbers with a bit of hexadecimal on it
https://ryanstutorials.net/binary-tutorial/
But, in short, binary numbers go from 1 to F
"1, 2, ..., 8, 9, A, B, C, D, E, F, 10, 11, 12, ....., 18, 19, 1A, 1B, 1C, 1D, 1E, 1F, 20, 21, 22..."
The F is like the 9 from decimal, after this you add one to the decimal place on the left and start again from zero, that is exactly how decimals work and that is how hexadecimal work too
So, by telling the instruction return(_pointer, 0x20)
we are telling the solidity assembly code to search, after the pointer, for the next 256bits of data (32 bytes), the 0x20
means, in hexadecimal, how many bytes, so it is 32 bytes, which turns out that it is 256bits (32*8), which is the size of uints as when you declare just uint
the system automatically considers it as uint256
Full loop with the return, all in assembly
Let us use what I have just shown you now and make the for loop from the previous class return the result but using assembly.
https://steemit.com/utopian-io/@igormuba/part-8-ethereum-solidity-assembly-reducing-costs-and-creation-your-low-level-expression-pt-8
The code was this
pragma solidity ^0.5.0;
contract solidityAssembly{
function assemblyLoop() public pure returns (uint _x){
assembly {
for {let counter:= 0} lt(counter, 10) {counter := add(counter,1)}{
_x := add(_x,1)
}
}
}
}
As you can see we create the returnable uint variable on the declaration of the function, which makes solidity automatically keep track of what happens to it and we edit it, now I want to change to code so the variable is created inside the assembly code and returned right from there
pragma solidity ^0.5.0;
contract solidityAssembly{
function assemblyLoop(uint _number) public pure returns (uint){ //we are not initiating the return like on last tutorial
assembly {
let _pointer:=add(msize(), 1) //initiates the pointer in the end of the memory
let _x:=_number //put the number in an internat variable, not required though
for {let counter:= 0} lt(counter, 10) {counter := add(counter,1)}{ //for loop from last class
_x := add(_x, 1) //at each run adds 1 to the given number (adds 10 in total)
}
mstore(_pointer, _x) //stores the final result on the pointer
return(_pointer, 0x20)//returns the result from the pointer plus 256bits after it
}
}
}
There is not much there to explain as it is just a merge from what I taught earlier on this tutorial plus what I taught on the previous tutorial
And it works just as expected
Playing with the memory
Let us say you want to store more than one number in the memory. To do that you, very likely, would want to have pen and paper next to your hand to keep track of the pointers, welcome to the low-level programming world!
First, here is the code, after that I will show you what happens, why and how
pragma solidity ^0.5.0;
contract solidityAssembly{
function assemblyLoop(uint _number, uint _numberTwo) public pure returns (uint){ //receives 2 varibles
assembly{
let _pointer:=add(msize(), 1) //first pointer
mstore(_pointer, _number) //stores on first pointer
let _pointerTwo:=add(_pointer, 0x20) //second pointer
mstore(_pointerTwo, _numberTwo) //stores on second pointer
let result:= add(mload(_pointer), mload(_pointerTwo)) //sums the 32byte stored on the 2 pointers
let _pointerThree:=add(_pointerTwo, 0x20) //third pointer
mstore(_pointerThree, result) //stores on third pointer
return(_pointerThree, 0x20) //returns 32bytes from third pointer
}
}
}
What happens is that, after we write something on the 32bytes from the pointer, we need to move to the next location, I did it the "lazy way", by creating new pointers each one in one location, the hardcore way of doing that would be using only one pointer
pragma solidity ^0.5.0;
contract solidityAssembly{
function assemblyLoop(uint _number, uint _numberTwo) public pure returns (uint){
assembly{
let _pointer:=add(msize(), 1)
mstore(_pointer, _number)
mstore(add(_pointer, 0x20), _numberTwo)
mstore(add(_pointer, 0x40), add(mload(_pointer), mload(add(_pointer, 0x20))))
return(add(_pointer, 0x40), 0x20)
}
}
}
We are storing the data in chunks of 32bytes, that is why the pointer moves 0x20
places at a time because the numbers are uint256.
Please, forgive me for my bad paint skills, but here is the space of memory we are using
On the first code, we were using 3 pointers, this allows us to use the pointers directly to store and retrieve data from the memory, one pointing to each space of memory, we plain simple filled each one with a 256bits of data from the uint256, which has the exact same size, 0x20 bytes
In the second example it is pretty much the same thing, but in this case we only have 1 pointer, so whenever we want to store new data we must pay very close attention to where are we writing data, we can corrupt the memory if we write anything on the wrong place, that is why I've told you that on low level we program with pens and papers beside us, to keep track of the pointer, physically!
fewer pointers less gas spent
After the previous explanation, you might think to yourself that having one pointer to each piece of data might be a good idea, but it is not! The advantage of storing raw data directly on the memory is to save gas cost, if we use pointers it won't change anything as we will still be using variables! So the less amount of pointers you can use the better, so the code with only one pointer is the cheapest one and I will show you.
I have created 2 functions on Remix, one with the use of 1 pointer and the other using 3 pointers
pragma solidity ^0.5.0;
contract solidityAssembly{
function assemblyLoop(uint _number, uint _numberTwo) public pure returns (uint){
assembly{
let _pointer:=add(msize(), 1)
mstore(_pointer, _number)
mstore(add(_pointer, 0x20), _numberTwo)
mstore(add(_pointer, 0x40), add(mload(_pointer), mload(add(_pointer, 0x20))))
return(add(_pointer, 0x40), 0x20)
}
}
function assemblyLoopTwo(uint _number, uint _numberTwo) public pure returns (uint){ //receives 2 varibles
assembly{
let _pointer:=add(msize(), 1) //first pointer
mstore(_pointer, _number) //stores on first pointer
let _pointerTwo:=add(_pointer, 0x20) //second pointer
mstore(_pointerTwo, _numberTwo) //stores on second pointer
let result:= add(mload(_pointer), mload(_pointerTwo)) //sums the 32byte stored on the 2 pointers
let _pointerThree:=add(_pointerTwo, 0x20) //third pointer
mstore(_pointerThree, result) //stores on third pointer
return(_pointerThree, 0x20) //returns 32bytes from third pointer
}
}
}
As you can see, they give the same results for the same inputs, quite obviously
But if you take a look at the execution cost
This first screenshot is for the code that uses only one pointer
This second screenshot is for the code that uses two pointers
As you can clearly see, by using only one pointer we save almost 10% of the gas cost, and that is the very reason why we use assembly
In comparison, if I build a function using higher level programming, no assembly, a pretty simple, fast and cheap function
function noAssembly(uint _number, uint _numberTwo) public pure returns (uint){
return _number+_numberTwo;
}
It still costs more than using 3 pointers
Sure, it costs almost the same as using 3 pointers, so we can take from this 2 things:
- Using fewer pointers is a very good thing.
- Using assembly, even ineffectively, is cheaper, even if only a little bit
But of course, not always the gas savings pays off for the extra work
Curriculum
- (Part 8) Ethereum Solidity - Assembly, Reducing Costs And Creation Your Low-Level Expression(PT 8)
- (Part 7) Ethereum Solidity - Fallback, Ethereum Fractions, And Collateral Backed Contract(PT 7)
- (Part 6) Ethereum Solidity - Custom Variable Functionalities, Libraries, Using Libraries For Security(PT 6)
- (Part 5) Ethereum Solidity - Custom Access Modifiers, Security Breach Alerts, Assert And Require(PT 5)
- (Part 4) Ethereum Solidity Development - Inheritance, Working With Multiple Contracts And Simple Mutability(PT 4)
- (Part 3) Ethereum Solidity Development - Contract Mutability, DelegateCall And Calling Functions By Address(PT 3)
- (Part 2) Ethereum Solidity Development - Deploying, Security And ERC20 Compliance(PT 2)
- (Part 1) Ethereum Solidity Development - Getting Started + Lower Level Explanation (PT 1)
Beneficiaries
This post has as beneficiaries
@utopian.pay with 5%
using the SteemPeak beneficiary tool
Thank you for your contribution @igormuba.
After analyzing your tutorial we suggest the following:
Thank you for continuing this excellent work.
We are waiting for your next tutorial.
Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.
To view those questions and the relevant answers related to your post, click here.
Need help? Chat with us on Discord.
[utopian-moderator]
Thank you for your review, @portugalcoin! Keep up the good work!
I would love you to host my Dtube videos. Please tell me if you are still providing this service. 😎
https://d.tube/v/acousticsteveo/wri9mha0
Yes, I am seeding DTube videos for free from my server, do you have discord or telegram?
Posted using Partiko Android
Yes, I am acousticsteveo on discord, and I have an acousticsteveo channel on telegram. You rock.
Posted using Partiko Android
Boa Igor sucesso com os tutoriais e os projetos. Estou acompanhando
Posted using Steeve, an AI-powered Steem interface
Hi @igormuba!
Your post was upvoted by @steem-ua, new Steem dApp, using UserAuthority for algorithmic post curation!
Your post is eligible for our upvote, thanks to our collaboration with @utopian-io!
Feel free to join our @steem-ua Discord server
Hey, @igormuba!
Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!
Get higher incentives and support Utopian.io!
Simply set @utopian.pay as a 5% (or higher) payout beneficiary on your contribution post (via SteemPlus or Steeditor).
Want to chat? Join us on Discord https://discord.gg/h52nFrV.
Vote for Utopian Witness!