Evitar los ciclos de propiedad en los contratos

Me enfrento repetidamente a este problema con la estructuración del código de los contratos:

contract Allower {
    Allowed allowed;

    function doSth() onlyallowed;

    modifier onlyallowed {
        if (msg.sender != address(allowed))
            throw;
        _
    }
}

contract Allowed {
    Allower allower;

    function doSthOnAllower() {
        allower.doSth();
    }
}

Para hacer cumplir que solo se Allowedejecuta una instancia particular de Allower.doSth, necesitamos que los contratos mantengan referencias mutuas (asumiendo que nos gusta la relación 1-1, pero aún queremos mantener estos contratos separados). Ahora, para implementar ambos contratos y que funcionen, necesitamos agregar, más o menos:

contract Allower {
    // snip...
    function Allower(Allowed _allowed) {
        allowed = _allowed;
        allowed.setAllower(this);
    }
}

contract Allowed {
    // snip...
    function setAllower(Allower _allower) allowernotset {
        allower = _allower;
    }
}

¿Existe algún patrón que permita eludir la necesidad de setAllower? Simplemente parece un antipatrón que se propaguen tales métodos "establecidos".

Respuestas (2)

Según aquí :

Tenga en cuenta que nos dio la nueva dirección del contrato. ¿De dónde salió esta dirección? Es el hash sha3 de la codificación RLP de la lista [dirección del remitente, número de secuencia del remitente].

Si sabe de antemano en qué momento de tx implementará Allowery puede hacer este cálculo de RLP usted mismo, entonces puede calcular la dirección para pasar al constructor de su Allowedcontrato. Y viceversa.

Reproduzcamos este script de Python en Javascript:

#!/usr/bin/nodejs

var ethJsUtil = require('ethereumjs-util');

var cuenta = "0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0";
console.log("nonce0= " + ethJsUtil.bufferToHex(ethJsUtil.generateAddress(cuenta, 0)));
console.log("nonce1= " + ethJsUtil.bufferToHex(ethJsUtil.generateAddress(cuenta, 1)));
console.log("nonce2= " + ethJsUtil.bufferToHex(ethJsUtil.generateAddress(cuenta, 2)));
console.log("nonce3= " + ethJsUtil.bufferToHex(ethJsUtil.generateAddress(cuenta, 3)));

Mi interés se disparó, también creé un pequeño proyecto Truffle aquí donde, para la implementación, hago:

módulo.exportaciones = función (implementador) {
    // Debe instalar npm -g ethereumjs-util
    var ethJsUtil = require('/usr/lib/node_modules/ethereumjs-util/');

    var currentNonce = web3.eth.getTransactionCount(web3.eth.accounts[0]);
    var futureLeftNonce = currentNonce;
    var futureLeftAddress = ethJsUtil.bufferToHex(ethJsUtil.generateAddress(
        web3.eth.accounts[0], futureLeftNonce));
    var futuroNonceDerecho = futuroNonceIzquierdo + 1;
    var futureRightAddress = ethJsUtil.bufferToHex(ethJsUtil.generateAddress(
        web3.eth.accounts[0], futureRightNonce));

    deployment.deploy(Izquierda, futuraDirecciónDerecha);
    deployment.deploy(Derecha, direcciónFuturaIzquierda);
};

No estoy seguro de hasta qué punto solucionaría el problema, pero uno puede considerar colocar la "relación 1-1" en un contrato separado, implementado antes de ambos Allowery Allowed.

contract Handcuffs {
    address left;
    address right;

    // set() must be called by both handcuffed contracts
    function set() {
        if (left == 0x0)
            left = msg.sender;
        else if (right == 0x0)
            right = msg.sender;
        else
            throw;
    }

    // once handcuffed, get() always returns the other contract
    function get() constant returns (address partner) {
        if (msg.sender == left && right != 0)
            return right;
        else if (msg.sender == right && left != 0)
            return left;
        else
            throw;
    }
}

contract Allower {
    Handcuffs allowed;

    function Allower(Handcuffs _allowed) {
        allowed = _allowed;
        allowed.set();
    }

    function doSth() onlyallowed;

    modifier onlyallowed {
        if (msg.sender != allowed.get())
            throw;
        _
    }
}

contract Allowed {
    Handcuffs allower;

    function Allowed (Handcuffs _allower) {
        allower = _allower;
        allower.set();
    }

    function doSthOnAllower() {
        var allowerContract = Allower(allower.get());
        allowerContract.doSth();
    }
}

Una alternativa a esto sería tener la llamada del implementador configurada explícitamente, liberando así Allowery Allowedde toda responsabilidad:

contract Handcuffs {
    // snip...
    function set(address _left, address _right) onlydeployer onlyonce {
        left = _left;
        right = _right;
    }
    // snip...
}