¿Cómo puede un contrato de fábrica clonar el código de bytes de un contrato existente?

Usar newpara crear un contrato hace que el contrato de fábrica se compile con el código de bytes del contrato del producto adjunto al final...

contract Foo
{
    function Foo() {}
}

bytecode: 60606040525b600056

contract FooFactory
{
    function newFoo() returns (Foo) {
       return new Foo();
    }
}

bytecode: 6060604052346000575b6092806100166000396000f3606060405260e060020a60003504639a67a7068114601c575b6000565b346000576026604f565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b6000604051602080607283396040519101819003906000f0801560005790505b90566060604052346000575b5b5b60098060176000396000f360606040525b600056                                                                        
                                                                                                                                                                                                                                                                                                                                        ^ Foo bytecode starts here

Para contratos de grandes fábricas/productos, esto puede convertirse en un problema de límite de gas en bloque al momento de la implementación.

¿Hay alguna forma de que un contrato de fábrica clone el código de bytes de un contrato existente?

implementar Foo y luego usar EXTCODECOPY en su dirección? ethereum.stackexchange.com/questions/1906/…
Gracias. Así que parece posible pero no trivial. Algún día tendré suficiente tiempo para estudiar en profundidad el montaje de EVM...

Respuestas (2)

¡Martin Holst Swende publicó una implementación de un contrato de clonación aquí! https://gist.github.com/holiman/069de8d056a531575d2b786df3345665

Extracto:

function clone(address a) returns(address){

    /*

    Assembly of the code that we want to use as init-code in the new contract, 
    along with stack values:
                    # bottom [ STACK ] top
     PUSH1 00       # [ 0 ]
     DUP1           # [ 0, 0 ]
     PUSH20         
     <address>      # [0,0, address] 
     DUP1       # [0,0, address ,address]
     EXTCODESIZE    # [0,0, address, size ]
     DUP1           # [0,0, address, size, size]
     SWAP4          # [ size, 0, address, size, 0]
     DUP1           # [ size, 0, address ,size, 0,0]
     SWAP2          # [ size, 0, address, 0, 0, size]
     SWAP3          # [ size, 0, size, 0, 0, address]
     EXTCODECOPY    # [ size, 0]
     RETURN 

    The code above weighs in at 33 bytes, which is _just_ above fitting into a uint. 
    So a modified version is used, where the initial PUSH1 00 is replaced by `PC`. 
    This is one byte smaller, and also a bit cheaper Wbase instead of Wverylow. It only costs 2 gas.

     PC             # [ 0 ]
     DUP1           # [ 0, 0 ]
     PUSH20         
     <address>      # [0,0, address] 
     DUP1       # [0,0, address ,address]
     EXTCODESIZE    # [0,0, address, size ]
     DUP1           # [0,0, address, size, size]
     SWAP4          # [ size, 0, address, size, 0]
     DUP1           # [ size, 0, address ,size, 0,0]
     SWAP2          # [ size, 0, address, 0, 0, size]
     SWAP3          # [ size, 0, size, 0, 0, address]
     EXTCODECOPY    # [ size, 0]
     RETURN 

    The opcodes are:
    58 80 73 <address> 80 3b 80 93 80 91 92 3c F3
    We get <address> in there by OR:ing the upshifted address into the 0-filled space. 
      5880730000000000000000000000000000000000000000803b80938091923cF3 
     +000000xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx000000000000000000
     -----------------------------------------------------------------
      588073xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx00000803b80938091923cF3

    This is simply stored at memory position 0, and create is invoked. 

    */
    address retval;
    assembly{
        mstore(0x0, or (0x5880730000000000000000000000000000000000000000803b80938091923cF3 ,mul(a,0x1000000000000000000)))
        retval := create(0,0, 32)
    }
    return retval;
}
No ejecuta lo que está dentro del constructor, ¿hay alguna nueva forma de hacerlo?

este código tiene una desventaja importante. No ejecuta el constructor del contrato fuente si presenta solidez de pragma ^0.4.25;

contract Factory {

function at(address _addr) private view returns (bytes memory o_code) {
    assembly {
        // retrieve the size of the code, this needs assembly
        let size := extcodesize(_addr)
        // allocate output byte array - this could also be done without assembly
        // by using o_code = new bytes(size)
        o_code := mload(0x40)
        // new "memory end" including padding
        mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
        // store length in memory
        mstore(o_code, size)
        // actually retrieve the code, this needs assembly
        extcodecopy(_addr, add(o_code, 0x20), 0, size)
    }
}

function create(address _addrOfCode) returns (address){
    address retval;
    assembly{
        mstore(0x0, or (0x5880730000000000000000000000000000000000000000803b80938091923cF3 ,mul(_addrOfCode,0x1000000000000000000)))
        retval := create(0,0, 32)
    }
    return retval;
}
}

contract Adder {
uint256 public param;

constructor(){
    param = 5;
}

function add(uint a, uint b) returns (uint){
    return a+b;
}
}

contract Tester {

Adder a;

function Tester(address factory,address adder){
    address cAdr = Factory(factory).create(adder);
    a = Adder(cAdr);
    if(address(a) == 0) throw;
}

function test(uint x, uint y) constant returns (uint){
    return a.add(x,y);
}
/* Reasonable expectation is 5, but it is 0 */
function getParam() constant returns (uint){
    return a.param();
}
}