7

I am repeatedly facing this issue with structuring contracts' code:

contract Allower {
    Allowed allowed;

    function doSth() onlyallowed;

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

contract Allowed {
    Allower allower;

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

In order to enforce that only a particular instance of Allowed executes Allower.doSth, we need the contracts to keep mutual references (assuming we like the 1-1 relationship, but still want to keep these contracts separate). Now, to deploy both contracts and have them work, we need to add, more or less:

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

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

Is there any pattern which would allow to by-pass the necesity of setAllower? It just seems like an anti-pattern to have such "set" methods propagate.

2 Answers 2

6

According to here:

Note it gave us the new contract address. Where did this address come from? It is the sha3 hash of the RLP encoding of the list [address of sender, sequence number of sender].

If you know in advance at what tx nonce you will deploy Allower and you can make this RLP calculation yourself, then you can calculate the address to pass to the constructor of your Allowed contract. And vice-versa.

Let's reproduce this Python script in Javascript:

#!/usr/bin/nodejs

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

var account = "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)));

My interest spiked, I also created a small Truffle project here where, for deployment I do:

module.exports = function(deployer) {
    // You need to npm install -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 futureRightNonce = futureLeftNonce + 1;
    var futureRightAddress = ethJsUtil.bufferToHex(ethJsUtil.generateAddress(
        web3.eth.accounts[0], futureRightNonce));

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

I am not sure to what extent it would fix the problem, but one can consider putting the "1-1 relationship" to a separate contract, deployed prior to both Allower and 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();
    }
}

An alternative to this would be to have the deployer call set explicitly, thus freeing Allower and Allowed from all responsibility:

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

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.