Избегание циклов владения в контрактах

Я неоднократно сталкиваюсь с этой проблемой при структурировании кода контрактов:

contract Allower {
    Allowed allowed;

    function doSth() onlyallowed;

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

contract Allowed {
    Allower allower;

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

Чтобы обеспечить выполнение только определенного экземпляра Allowed, Allower.doSthнам нужно, чтобы контракты сохраняли взаимные ссылки (при условии, что нам нравится отношение 1-1, но мы все же хотим, чтобы эти контракты были отдельными). Теперь, чтобы развернуть оба контракта и заставить их работать, нам нужно добавить более или менее:

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

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

Есть ли какой-нибудь шаблон, который позволил бы обойти необходимость setAllower? Это просто похоже на анти-шаблон распространения таких "установленных" методов.

Ответы (2)

Согласно здесь :

Обратите внимание, что он дал нам новый адрес контракта. Откуда взялся этот адрес? Это хэш sha3 RLP-кодирования списка [адрес отправителя, порядковый номер отправителя].

Если вы заранее знаете, на какой tx nonce вы будете деплоить, Allowerи можете самостоятельно произвести этот расчет RLP, то вы можете рассчитать адрес для передачи конструктору вашего Allowedконтракта. Наоборот.

Давайте воспроизведем этот скрипт Python в Javascript:

#!/USR/бен/узлы

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

учетная запись var = "0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0";
console.log("nonce0= " + ethJsUtil.bufferToHex(ethJsUtil.generateAddress(account, 0)));
console.log("nonce1= " + ethJsUtil.bufferToHex(ethJsUtil.generateAddress(account, 1)));
console.log("nonce2= " + ethJsUtil.bufferToHex(ethJsUtil.generateAddress(account, 2)));
console.log("nonce3= " + ethJsUtil.bufferToHex(ethJsUtil.generateAddress(account, 3)));

Мой интерес резко возрос, я также создал здесь небольшой проект Truffle, где для развертывания я делаю:

module.exports = функция (развертыватель) {
    // Вам нужно установить npm -g ethereumjs-util
    var ethJsUtil = require('/usr/lib/node_modules/ethereumjs-util/');

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

    deployer.deploy(Left, futureRightAddress);
    deployer.deploy(Right, futureLeftAddress);
};

Я не уверен, в какой степени это решит проблему, но можно подумать о том, чтобы поместить «отношения 1-1» в отдельный контракт, развернутый до обоих Allowerи 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();
    }
}

Альтернативой этому может быть явно установленный вызов deployer, что освобождает Allowerот Allowedвсякой ответственности:

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