When a method in a deployed contract is called (either by an externally controlled account or by another smart contract), what will the program counter be at the beginning of the method call? In other words: Where will code execution start and how does the Ethereum client and the other contracts know where the different methods are located in the EVM?
2 Answers
The program counter is always 0 at the beginning of an execution. The contract sets the program counter to match the correct method call through a switch statement (a jump table) at the beginning of the execution of a contract. The CALLDATALOAD
opcode (with argument 0x00 on the stack) gets the input data for the method call onto the stack. The first input data for a method call is the method signature which is the first four bytes of SHA3(<methodname>(methodArgumentType))
.
For a method called transfer
which takes a uint8 as argument, this would be the first four bytes (big endian, left-most bytes, of SHA3('transfer(uint8)')
which is "0x865645aa". The text string of the input to the SHA3 function is encoded as ASCII.
The function signature is compared to the function signature of the available methods in this contract, and control is then given to that method through a conditional jump instruction (JUMPI).
The calling convention for invoking contract methods is defined in the Contract ABI Specification.
A switch-like statement at the beginning of the contract is how the Solidity compiler gives control to the correct method. If you write your own compiler or your own EVM, the important thing is to follow the Contract ABI Specification (if you want it to be easy for other people, programs, and contracts to interact with your code).
Illustrating more of @Thorkil's answer.
Example
A very rough example, for conceptual purposes only, of the "switch-like statement at the beginning of the contract".
method_id = first 4 bytes of msg.data
if method_id == 0x25d8dcf2 jump to 0x11
if method_id == 0xaabbccdd jump to 0x22
if method_id == 0xffaaccee jump to 0x33
other code <- Solidity fallback function code could be here
0x11:
code for function with method id 0x25d8dcf2
0x22:
code for another function
0x33:
code for another function
One can see that the first 4 bytes of msg.data
(Method ID is the term given by the ABI) is used to check which function to jump to and execute.
The Contract ABI Specification is a convention outside of the EVM
From the example, you can also see that you can have your own compiler generate bytecode with different logic (maybe you want to use the first 8 bytes of msg.data), but callers would then have to follow those conventions instead of simply using a library like ethers or web3.js. It is very helpful that everyone follows the same ABI, but for example, you can write your own bytecode and tools that could make it difficult for anyone else to call your contract. (But security by obscurity may not offer much since reverse engineering is possible with all data on the blockchain being public).