the evm

persistent storage

The persistent memory associated with each address is called storage. This is a key-value store, mapping 256-bit words to 256-bit words. It cannot be enumerated from inside a contract, and contracts have no access or view of the storage associated with other addresses.

If you initialise variables as in uint256 blah; , this will save blah to storage. uint is an alias of uint256 , and bytes can also be assigned on a more fine-grained level, using uint8 , uint16 , etc.

volatile memory

The EVM has a virtual stack with which to store 256 bit values. 256-bit words were chosen for compatibility with cryptographic operations. All EVM operations are performed using this virtual stack. The maximum number of elements the stack can contain is 1024. You can copy one of the top 16 elements, or swap the top element with one of the 16 below (meaning you can here access the 17th highest value on the stack). All other opcodes take the pre-determined number of elements from the top of the stack and then push the return value onto the stack.

Volatile memory is received as a freshly cleared instance for each message call. Memory is allocated in words, and gas is used to pay for the expansion of memory to hold the amount of words you’re storing. We need the values with which we want to call a precompile to be sat at the top of this memory.

We can assign variables previously stored in storage to memory in the following way:

uint256[2] memory inputToPrecompile;

input[0] = somePreviouslyStoredValue;

input[1] = someOtherPreviouslyStoredValue;

This is, in fact, exactly what we’re doing with the first four lines in ecmul . We are pushing the values ax , ay , and k to the top of the virtual stack. The precompile is then immediately called, by invoking the address where the code necessary to perform a bn256ScalarMul operation is sat. Looking at the next section of code, we see:

assembly {

if iszero(staticcall(gas, 0x07, input, 0x60, p, 0x40)) {

revert(0,0)

}

}

The staticcall opcode is called with the following:

staticcall(gasLimit, to, inputOffset, inputSize, outputOffset, outputSize)

We see then that, in the case of the bn256ScalarMul -calling code above, we are:

Sending the amount of gas currently available to us, after subtracting 2000;

Calling the contract at address 0x07 , which the mapping at the top tells us corresponds to bn256ScalarMul ;

, which the mapping at the top tells us corresponds to ; Defining the input offset as input , as we have just declared in memory;

, as we have just declared in memory; Declaring the input size as 0x60 , corresponding to a value of three 256 bit words, exactly the size of an elliptic curve point and one 256 bit scalar;

, corresponding to a value of three 256 bit words, exactly the size of an elliptic curve point and one 256 bit scalar; the output will be stored at value p ; and

; and the output size is 0x40 , corresponding to the elliptic curve point that will be returned to us.