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 Allowed
ejecuta 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".
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á Allower
y puede hacer este cálculo de RLP usted mismo, entonces puede calcular la dirección para pasar al constructor de su Allowed
contrato. 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 Allower
y 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í Allower
y Allowed
de toda responsabilidad:
contract Handcuffs {
// snip...
function set(address _left, address _right) onlydeployer onlyonce {
left = _left;
right = _right;
}
// snip...
}