Tengo la siguiente configuración aquí, simplificada, pero aún demuestra mi problema.
Supongamos que tengo un UsuarioA y un UsuarioB que tienen una billetera y un contrato inteligente respectivamente. Cada usuario implementa su propio contrato inteligente.
UsuarioA.sol
pragma solidity ^0.4.2;
contract UserA {
address public owner;
address public user;
address public SCAddress; //address where UserB has mined his contract
function UserA(string _provider,address _user,address _scaddress) {
owner = msg.sender;
user = _user;
SCAddress = _scaddress;
}
function setColor(string c) { //want to set the color of UserB from here
if (msg.sender == user) { // check if it is userA
color = c;
}
}
UsuarioB.sol :
pragma solidity ^0.4.2;
contract UserB {
address public owner;
address public user; //userA wallet address
string public color;
function UserB(address _user) {
owner = msg.sender;
user = user;
}
function chooseColor(int number) constant returns (string color) {
if(number == 1){ return "red";}
else if(number == 2){ return "blue";}
else if(number == 3){ return "green";}
}
}
UsuarioB.js
var Web3 = require('web3');
//connect to testRPC / Geth locally
if (typeof web3 !== 'undefined') {
web3 = new Web3(web3.currentProvider);
} else {
// set the provider you want from Web3.providers
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}
// Checking Ethereum connection status
if (!web3.isConnected()) {
console.error("Ethereum - no conection to RPC server");
} else {
console.log("Ethereum - connected to RPC server");
}
var userbABI = ;// lets assume I have it
var userbAddress = ;// lets assume I have it
var userbContract = web3.eth.contract(userbABI);
var userbSC = docContract.at(userbAddress);
var result = userbSC.chooseColor.call(1); //red
//Now I want to save this result with UserA calling setColor()
userA.setColor(result);
Mis restricciones:
color
.setcolor()
través de la ABI del Usuario A.Mis preguntas:
Sé en este momento que UserB no puede llamar setColor()
desde msg.owner
=/= UserB sino a UserA .
Gracias por su valioso tiempo y ayuda.
No estoy seguro de que esto sea lo que quieres, pero podría darte algunas ideas sobre cómo estructurar las cosas.
Solo necesita implementar el contrato ColorPallet. Cualquier dirección desde la que se implemente será el propietario. Solo el propietario puede ampliar la lista cada vez mayor de posibles opciones de color.
El ColorPallet también es una fábrica. Cualquiera puede venir y usar la función newUser() para implementar un contrato de usuario. La propiedad de ese contrato se da a la dirección que lo solicitó. Entonces el "usuario" tiene el control completo del "Usuario".
El "usuario" puede establecer un color. Tiene que estar dentro del alcance y va a comprobar. Entonces, si hay 3 colores, son 0, 1 y 2. 3 o más arrojarán. El administrador puede agregar más opciones.
Tengo el contrato de Usuario almacenando la selección localmente, y también empujando la elección a ColorPallet para mostrar cómo el Usuario puede establecer un valor en un contrato diferente. En este caso, enviando un mensaje.
Descargo de responsabilidad: No hay muchas pruebas. Solo quería mostrar contratos hablando entre sí y un patrón para controlar el acceso a las funciones. ;-)
Espero eso ayude.
pragma solidity ^0.4.6;
contract ColorPallet {
address public owner; // this user has admin control
// bytes32 is a better choice in the long run
bytes32[] public colors;
mapping(address => uint) public userColorChoices;
mapping(address => bool) public userHasSelectedColor;
modifier onlyOwner() {
if(msg.sender != owner) throw;
_;
}
function ColorPallet() {
owner = msg.sender;
}
// only the admin can create a new color
function newColor(bytes32 colorName)
onlyOwner
returns(uint index)
{
colors.push(colorName);
return colors.length-1;
}
// contract "User" sets values here. They are organized with different data per user
function setUserColor(uint colorId)
public
{
if(colorId >= getColorCount()) throw; // too high
userColorChoices[msg.sender] = colorId; // msg.sender is the User contract in this context
userHasSelectedColor[msg.sender] = true;
}
function getColorCount()
public
constant
returns(uint count)
{
return colors.length;
}
function newUserContract()
public
returns(address newUserContract)
{
User u = new User(msg.sender, this); // we give ownership to msg.sender, the pallet is "this address"
return u;
}
}
contract User {
address public owner; // this is the user wallet that deployed this contract
address public pallet;
uint public myColor;
ColorPallet c;
modifier onlyOwner() {
if(msg.sender != owner) throw;
_;
}
function User(address userOwner, address pallet) {
// owner is passed in, so it's the wallet that asked ColorPallet for a new "User" contract
owner = userOwner;
// the pallet is the one who asked for this contract to be deployed
c = ColorPallet(pallet);
}
function selectColor(uint colorId)
//onlyOwner
returns(bool success)
{
myColor = colorId; // set a valid choice locally in this contract
c.setUserColor(colorId); // set a valid choice externally in the other contract
return true;
}
}
Implemente ColorPallet y agregue algunos colores:
Solicite un usuario y anote la dirección implementada:
Ya está desplegado. Cree una instancia del contrato en la dirección desplegada.
Elija un color válido (dentro del rango) y vea el almacenamiento actualizado.
Compruebe el almacenamiento de ColorPallet. Ponga la dirección del contrato de Usuario en la función captadora pública para el mapeo:
tx.origin
. Traté de entender su código ayer, pero todavía necesito ayuda con estas preguntas: - ¿Está mapping(address => uint) public userColorChoices;
almacenando solo la última opción del usuario o es una matriz que ha almacenado todas las opciones del usuario desde el principio? - ¿Cuál es la diferencia entre retorno y devoluciones? Muchas gracias. PD: No sé, pero tal vez debería abrir una sala para hablar de esto.Es posible conectar dos contratos inteligentes entre sí. Al comienzo del archivo, debe importar el contrato de manera muy similar a otros idiomas. Luego puede configurar el otro contrato como un objeto.
UsuarioB.sol :
import "./UserA.sol";
contract UserB {
...
UserA userA
...
function UserB(address userA) {
userA = UserA(userA);
}
Ahora puede llamar a métodos en "userA" como userA.setColor("blue");
userB
puede llamar setColor()
y userA
solo puede modificar color
.UserB
despliega el contacto inteligente B, y luego configura el color. Solo UserB
conoce el color de entrada para A. Es por eso que UserB
necesita llamar UserA
a la función para establecer el color.Esto es absolutamente posible. Simplemente podría cambiar su UserB
un poco.
Puede enviar una dirección a un contrato y viceversa (pero debe asegurarse de que el contrato real en esa dirección coincida, de lo contrario se encontraría con una excepción de tiempo de ejecución):
contract UserB {
address public owner;
UserA public user; //userA wallet address
string public color;
function UserB(address _user) {
owner = msg.sender;
user = UserA(_user);
}
function delegateCall(string c) {
user.setColor(c);
}
}
Sin embargo, su ejemplo no es tan fácil: cuando llama UserA.setColor
desde UserB.delegateCall
entonces, msg.sender
es la dirección de UserB
y no usted (el origen de la transacción). Podría usar, tx.origin
pero realmente no debería hacerlo y es posible que se elimine en versiones futuras . Puede verificar cómo se maneja esto generalmente en billeteras/tokens, por ejemplo, aquí eche un vistazo a approve
y transferFrom
. Podría, por ejemplo, establecer su propiedad UserB
como propietario de UserA
creando una instancia de UserA
en el constructor de UserB
.
tx.origin
y tenía buenas esperanzas, tal vez incluso pensé "esto es, esto es lo que realmente necesito" . Pero ahora, estoy tan confundido, porque quiero exactamente lo contrario de lo que me dijiste. UserB
debe ser el "esclavo" de UserA
.
Rob Hitchens