3

The following code snippet is a constituent piece of the instructions.go file, where our lieblings opCodes all live.

// make log instruction function
func makeLog(size int) executionFunc {
    return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
        topics := make([]common.Hash, size)
        mStart, mSize := stack.pop(), stack.pop()
        for i := 0; i < size; i++ {
            topics[i] = common.BigToHash(stack.pop())
        }

        d := memory.Get(mStart.Int64(), mSize.Int64())
        evm.StateDB.AddLog(&types.Log{
            Address: contract.Address(),
            Topics:  topics,
            Data:    d,
            // This is a non-consensus field, but assigned here because
            // core/state doesn't know the current block number.
            BlockNumber: evm.BlockNumber.Uint64(),
        })

        evm.interpreter.intPool.put(mStart, mSize)
        return nil, nil
    }
}

// make push instruction function
func makePush(size uint64, pushByteSize int) executionFunc {
    return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
        codeLen := len(contract.Code)

        startMin := codeLen
        if int(*pc+1) < startMin {
            startMin = int(*pc + 1)
        }

        endMin := codeLen
        if startMin+pushByteSize < endMin {
            endMin = startMin + pushByteSize
        }

        integer := evm.interpreter.intPool.get()
        stack.push(integer.SetBytes(common.RightPadBytes(contract.Code[startMin:endMin], pushByteSize)))

        *pc += size
        return nil, nil
    }
}

// make push instruction function
func makeDup(size int64) executionFunc {
    return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
        stack.dup(evm.interpreter.intPool, int(size))
        return nil, nil
    }
}

// make swap instruction function
func makeSwap(size int64) executionFunc {
    // switch n + 1 otherwise n would be swapped with n
    size += 1
    return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
        stack.swap(int(size))
        return nil, nil
    }

The question is, are these make log instruction functions themselves opcodes?

If they are- why are they not called as such?

If not- what are they doing living in the file with all of the opCodes?

1 Answer 1

3

Not sure if I've fully understood your question, but here are my observations...

The opcodes handled by the four make* functions you list differ from the other EVM opcodes in that they are each part of wider opcode families:

  • makeLog() handles opcodes LOG0 thru LOG4

  • makePush() handles opcodes PUSH1 thru PUSH32

  • makeDup() handles opcodes DUP1 thru DUP16

  • makeSwap() handles opcodes SWAP1 thru SWAP16

This differs from the other functions in the file, each of which handles only a single opcode, along with its stack operands.

In addition, the LOG* opcodes are particularly unusual since each one consumes a different number of stack operands: LOG0 consumes 2, LOG4 consumes 6. So this needs to be dealt with as well.

In the end, the function returned by each of the make*() meta-functions will perform the work of a single opcode, and is therefore on an equal footing with the other functions in the file.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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