Я неоднократно сталкиваюсь с этой проблемой при структурировании кода контрактов:
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
? Это просто похоже на анти-шаблон распространения таких "установленных" методов.
Согласно здесь :
Обратите внимание, что он дал нам новый адрес контракта. Откуда взялся этот адрес? Это хэш 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...
}