ethereumsolidityopcode

Adding new custom opcode to solidity


I have a problem with adding new opcode to solidity. I'm using solc (on C++) and geth(ethereum on Go). I want to add new opcode, that takes address payable, uint256, uint256, bytes memory and returns bytes memory. So I have a problem with return value.

Some peaces of code below, I will skip some files, to make question shorter.

  1. Solc
// ExpressionCompiler::visit(FunctionCall const& _functionCall)
case FunctionType::Kind::MyOpcode:
{
    acceptAndConvert(*arguments[0], *function.parameterTypes()[0], true);
    acceptAndConvert(*arguments[1], *function.parameterTypes()[1], true);
    acceptAndConvert(*arguments[2], *function.parameterTypes()[2], true);
    arguments[3]->accept(*this);
    utils().fetchFreeMemoryPointer();
    utils().packedEncode(
        {arguments[3]->annotation().type},
        {TypeProvider::array(DataLocation::Memory, true)}
    );
    utils().toSizeAfterFreeMemoryPointer();

    m_context << Instruction::MYOPCODE;
}
// inline vector<shared_ptr<MagicVariableDeclaration const>> constructMagicVariables()
magicVarDecl("myopcode", TypeProvider::function(strings{"address payable", "uint256", "uint256", "bytes memory"}, strings{"bytes memory"}, FunctionType::Kind::MyOpcode, false, StateMutability::Payable)),
// static std::map<Instruction, InstructionInfo> const c_instructionInfo =
{ Instruction::MYOPCODE,       { "MYOPCODE",          0, 5, 1, true, Tier::Base } }
  1. Geth
// func newFrontierInstructionSet() JumpTable {
CALLACTOR: {
    execute:    opMyOpCode,
    dynamicGas: gasCallActor,
    minStack:   minStack(5, 1),
    maxStack:   maxStack(5, 1),
    memorySize: memoryReturn,
    writes:     true,
    returns:    true,
},
func opMyOpcode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
    inoffset, insize := callContext.stack.pop(), callContext.stack.pop()
    params := callContext.memory.GetPtr(int64(inoffset.Uint64()), int64(insize.Uint64()))

    secondValue := callContext.stack.pop()

    firstValue := callContext.stack.pop()

    addr := callContext.stack.pop()

    // ... Do smth with input ...

    outoffset := inoffset.Uint64() + insize.Uint64()

    callContext.memory.Set(outoffset, 1, []byte{0x2})
    tmp := make([]byte, 1)
    tmp[0] = 0x98
    callContext.memory.Set(outoffset + 1, 1, tmp)

    callContext.stack.push(uint256.NewInt().SetUint64(outoffset))

    return tmp, nil
}
  1. Smart contract
pragma solidity >=0.6.0; // SPDX-License-Identifier: GPL-3
contract test {
    event ReturnValue(address payable _from, bytes data);
    function f() public returns(bytes memory){
        address payable addr1 = payable(msg.sender);
        bytes memory params = new bytes(2);
        params[0] = 0x21;
        params[1] = 0x22;
        bytes memory result = myopcode(addr1, 0x11, 0x12, params);
        emit ReturnValue(addr1, result);
        return result;
    }
}

When I run that code I get invalid jump destination. So, what I need to do, to get my code work correctly?


Solution

  • I found a solution, it certainly looks stupid, but this case is already provided by the developers. All you need is utils().returnDataToArray().