8

I read the source code of go-ethereum and confused about how does the EVM find the entry of a called function. As the specification said, the data field in a transaction specifies the function and the arguments. So, in my comprehension, the specific function should be executed in the EVM with related input data rather than the whole contract code. However, I can't find the related code. In the execution loop, PC starts from 0:

pc = uint64(0) // program counter
for ; ; instrCount++ {



    // Get the memory location of pc
    op = contract.GetOp(pc)
    // calculate the new memory size and gas price for the current executing opcode
    newMemSize, cost, err = calculateGasAndSize(evm.env, contract, caller, op, statedb, mem, stack)
    if err != nil {
        return nil, err
    }

    // Use the calculated gas. When insufficient gas is present, use all gas and return an
    // Out Of Gas error
    if !contract.UseGas(cost) {
        return nil, OutOfGasError
    }

    // Resize the memory calculated previously
    mem.Resize(newMemSize.Uint64())
    // Add a log message
    if evm.cfg.Debug {
        evm.logger.captureState(pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), nil)
    }

    if opPtr := evm.jumpTable[op]; opPtr.valid {
        if opPtr.fn != nil {
            opPtr.fn(instruction{}, &pc, evm.env, contract, mem, stack)
        } else {
            switch op {
            case PC:
                opPc(instruction{data: new(big.Int).SetUint64(pc)}, &pc, evm.env, contract, mem, stack)
            case JUMP:
                if err := jump(pc, stack.pop()); err != nil {
                    return nil, err
                }

                continue
            case JUMPI:
                pos, cond := stack.pop(), stack.pop()

                if cond.Cmp(common.BigTrue) >= 0 {
                    if err := jump(pc, pos); err != nil {
                        return nil, err
                    }

                    continue
                }
            case RETURN:
                offset, size := stack.pop(), stack.pop()
                ret := mem.GetPtr(offset.Int64(), size.Int64())

                return ret, nil
            case SUICIDE:
                opSuicide(instruction{}, nil, evm.env, contract, mem, stack)

                fallthrough
            case STOP: // Stop the contract
                return nil, nil
            }
        }
    } else {
        return nil, fmt.Errorf("Invalid opcode %x", op)
    }

    pc++

}

So how does the EVM execute the specific function?

7

the specific function should be executed in EVM with related input data rather than the whole contract code

  1. The EVM will execute the contract code. The EVM just executes the bytecode and does not know anything about functions. Solidity, Serpent and web3.js implement the same Application Binary Interface, which is how functions and data are encoded: What is an ABI and why is it needed to interact with contracts?

As noted here, the ABI is an abstraction on top of the Ethereum protocol and the EVM.

  1. An EVM compiler produces contract code that simulates functions according to the ABI. Here's an example.

Example

For functions that adhere to the ABI, it's the job of the compiler to produce correct EVM bytecode.

A very rough example, for conceptual purposes only, of the bytecode that an EVM compiler would produce:

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 function with method id 0xaabbccdd
0x33:
code for function with method id 0xffaaccee

You 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.

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 web3.js.

Example is from:

When does the fallback function get called?

How is an ABI stored in bytecode?

  • So EVM just run the bytecodes. for example, i call a contract function func, and the opCalldataLoad function will load the input data and may jump to the related location using the function signature in input data and then finish the evm execution when RETURN opcode be met. – rong jialei Aug 1 '16 at 3:22
  • Yes, that sounds reasonable. – eth Aug 1 '16 at 4:55
5

I'm using the modified example code from Syntax for calling contract state changing methods, saving it in C.sol:

contract C {
    uint[] public numbers;

    function initNumbers() {
         numbers.push(1);
         numbers.push(2);
    }

    function stateChanger(uint a) {
         numbers.push(a);
    }
}

Following is the compiled binary code in hex format, formatted into 80 character columns. You can see the 3 function signatures within the binary code marked with '<' and '>'.

user@Kumquat:~$ solc --abi --hashes --bin C.sol

======= C =======
Binary:
6060604052610217806100126000396000f360606040526000357c01000000000000000000000000
0000000000000000000000000000000090048063<5a7dc897>1461004f578063<65060775>1461005e57
8063<d39fa233>146100765761004d565b005b61005c60048050506100a2565b005b61007460048080
35906020019091905050610181565b005b61008c60048080359060200190919050506101f2565b60
40518082815260200191505060405180910390f35b60006000508054806001018281815481835581
8115116100f4578183600052602060002091820191016100f391906100d5565b808211156100ef57
600081815060009055506001016100d5565b5090565b5b5050509190906000526020600020900160
005b6001909190915055506000600050805480600101828181548183558181151161016257818360
0052602060002091820191016101619190610143565b8082111561015d5760008181506000905550
600101610143565b5090565b5b5050509190906000526020600020900160005b6002909190915055
505b565b600060005080548060010182818154818355818115116101d35781836000526020600020
91820191016101d291906101b4565b808211156101ce57600081815060009055506001016101b456
5b5090565b5b5050509190906000526020600020900160005b83909190915055505b50565b600060
005081815481101561000257906000526020600020900160005b91509050548156

Function signatures:
5a7dc897: initNumbers()
65060775: stateChanger(uint256)
d39fa233: numbers(uint256)

Contract JSON ABI
[{"constant":false,"inputs":[],"name":"initNumbers","outputs":[],"type":"function"},
{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"stateChanger","outputs":[],"type":"function"},
{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"numbers","outputs":[{"name":"","type":"uint256"}],"type":"function"}]

Here's how the function signatures are computed in geth:

> web3.sha3('initNumbers()').substr(0,10)
"0x5a7dc897"
> web3.sha3('stateChanger(uint256)').substr(0,10)
"0x65060775"
> web3.sha3('numbers(uint256)').substr(0,10)
"0xd39fa233"
  • If i want to detect the balance change in contract, How can i do that. Should a start a new question to ask that? – rong jialei Aug 1 '16 at 3:26
  • Yup. New question is the best way. – The Officious BokkyPooBah Aug 1 '16 at 4:19
4

As BokkyPooBah points out, the function signature is used to find the place in the bytecode that contains the code for the called method. The Ethereum ABI defines how the arguments are passed. The first four bytes of the provided argument string is the function signature, encoded using Keccak (SHA3). A switch-like statement is used to compare the first four bytes of the provided argument string to the methods available for this contract. If no match is found, the contracts throws an exception (by calling a non-existing opcode) and the method call then fails. If a match is found, the program counter is moved to that method by using the JUMPI opcode.

See also What is the program counter at the start of an Ethereum method execution?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.