Creando un contrato con constructor en ethereumj

contract Calculator{
    int result;
    function Calculator(int initialResult){
       result=initialResult;
    }
}

Si arriba está el código fuente del contrato que he creado, entonces cómo llamar a la función Calculadora usando la API ethereumj. ya he usado

contract.callFunction("Calculator", 10)

Pero esto no funciono. También llamado

contract.callConstFunction("Calculator", 10)

Pero esto tampoco funcionó.

Y cuando vio el ABI, tenía el tipo de función = "constructor", pero no pudo encontrar ningún método que represente llamar al constructor.

Sugiera si hay alguna forma de llamar a las funciones del constructor.

Respuestas (3)

Finalmente encontré una solución. Como se discutió en la otra respuesta, debe analizar y agregar los parámetros de inicio al código del contrato antes de enviar la transacción. En realidad, no encontré una forma elegante y genérica de analizar argumentos directamente en Java (pero no debería ser difícil hacerlo), así que decodifiqué la sintaxis ABI en byte[].

En primer lugar, copie StandaloneBlockchain.java y agregue un método (lo siento, no puedo encontrar una manera fácil de hacerlo a través de la extensión):

public SolidityContract submitNewContract(String soliditySrc, String contractName, byte[] initParameters) {
    SolidityContractImpl contract = createContract(soliditySrc, contractName);
    submitNewTx(new PendingTx(new byte[0], BigInteger.ZERO, ByteUtil.merge(Hex.decode(contract.getBinary()), initParameters), contract, null));
    return contract;
}

Luego, en StandaloneBlockchainSample.java, consulte la clase modificada, modifique el contrato de muestra para tener 2 parámetros en el constructor (no tuve tiempo de verificar todos los tipos, pero debería funcionar sin problemas) y asigne los valores de inicio de los parámetros:

// Pretty simple (and probably the most expensive) Calculator
private static final String contractSrc =
        "contract Calculator {" +
        "  int public result;" +  // public field can be accessed by calling 'result' function
        "  string testString;" +
        "  bool testBool;"+
        "  function add(int num) {" +
        "    result = result + num;" +
        "  }" +
        "  function sub(int num) {" +
        "    result = result - num;" +
        "  }" +
        "  function mul(int num) {" +
        "    result = result * num;" +
        "  }" +
        "  function div(int num) {" +
        "    result = result / num;" +
        "  }" +
        "  function clear() {" +
        "    result = 0;" +
        "  }" +
        "  function getString() constant returns (string) { return testString; }" +
        "  function getBool() constant returns (bool) { return testBool; }" +
        "  function Calculator(string _testString, bool _testBool){" +
        "    testString = _testString;" +
        "    testBool = _testBool;" +
        "  }" +
        "}";

[...]

String parameters = "{ 'inputs': [ { 'name': '_test', 'type': 'string' }, { 'name': '_test2', 'type': 'bool' } ] }".replaceAll("'", "\"");
byte[] initParameters = CallTransaction.Function.fromJsonInterface(parameters).encodeArguments("Test", true);
SolidityContract calc = bc.submitNewContract(contractSrc, null, initParameters);
System.out.println("Calculating...");

Después de eso, verifique el valor correcto de los parámetros con:

assertEqual("Test", (String) calc.callConstFunction("getString")[0]);
assertEqual(true, (boolean) calc.callConstFunction("getBool")[0]);

Tenga en cuenta que debe modificar el método privado assertEqual dentro de la clase para afirmar otros tipos además del BigInteger ya definido. Probé esta solución con EthereumJ 1.2.0: tal vez los próximos lanzamientos incluyan una mejor manera de pasar los valores de inicio al constructor, así que actualice la respuesta si lo sabe.

No necesita llamar al constructor, debe enviar el contrato y automáticamente tendrá un puntero al contrato, de esta manera:

StandaloneBlockchain bc = new StandaloneBlockchain().withAutoblock(true);
bc.createBlock();
SolidityContract contract = bc.submitNewContract(contractSrc);
System.out.println(contract.callConstFunction("result")[0]);

Puede ver un ejemplo de trabajo simple pero completo en github

He usado ese ejemplo y sé que el ejemplo funciona sin constructor. Pero es necesario utilizar un contrato de solidez con el constructor, consulte el ejemplo de Crowdsale o cree su propia moneda criptográfica en ethereum.org. Si revisa la billetera Mist, le permite crear contratos con el constructor, por lo que su respuesta no resuelve mi problema. Todavía necesito que alguien me diga cómo llamar al constructor del contrato que he creado. Gracias por tomarte el tiempo de responder.
Tal vez entendí mal tu pregunta. En su ejemplo, Calculator es un constructor porque tiene el mismo nombre que el contrato. Hasta donde yo sé, no puede llamar a los constructores desde fuera de EVM (es decir, desde el cliente en Java, Web3, etc.) sino desde adentro con instrucciones como "Calculator calc = new Calculator()" dentro de un método de contrato. Se llama al constructor durante la fase de creación cuando implementa un contrato. Puede hacer referencia a un contrato implementado para llamar a métodos. Todas esas llamadas a métodos son luego traducidas por el cliente (Java en su caso) en transacciones para ser enviadas a la red.
Sí. Tal vez tengas razón. Pero entonces debería haber alguna forma de pasar los argumentos mientras se envía el constructor. Esto debería haber estado allí, pero es sorprendente que no esté allí y los primeros ejemplos en ethereum.org explican el uso de constructores. Y como sugirió "Calculator calc = new Calculator ()", no parece correcto, porque luego tiene que codificar los argumentos, lo que en sí mismo no es una buena práctica. Una cosa más es que cuando verifica el ABI, contiene el argumento y el tipo de función como constructor, pero no el nombre del constructor. Gracias
Ahora veo. El problema es cómo pasar los valores de inicialización durante la fase de implementación del contrato. En Web3 esto es realmente simple (es decir, MyContract.new(param1, param2, ...)) pero en EthereumJ no veo una manera fácil de hacerlo. Bajo el capó, para pasar el valor de inicio a un constructor, debe "codificar ABI" esos parámetros y agregar bytes a los bytes del contrato, de modo que los bytes de datos de la transacción contengan código + parámetros. En particular, todo debe colocarse dentro de la matriz de bytes org.ethereum.core.Transaction.data antes de enviar. Profundizaré más, parece un apéndice muy útil para tener una manera fácil de pasar parámetros
Gracias. También lo investigaré. También intentaré sugerir más cambios.

Agregado a 1.3.0-RC4:

SolidityContract a = sb.submitNewContract(
  "contract A {" +
  "  uint public a;" +
  "  uint public b;" +
  "  function A(uint a_, uint b_) {a = a_; b = b_; }" +
  "}", 555, 777);
¿Qué se agregó? ¿Podría explicar de qué se trata su respuesta?
@5chdn se generó un ticket en el flujo de desarrollo de ethereumj con respecto a tener una instalación para llamar directamente al constructor con argumentos iniciales, por lo que se agregó una solución al flujo, aún no lo he probado, Anton ha mencionado sobre los cambios para el mismo, así que supongo que merece votos positivos en lugar de negativos. :)
Solo pensé que un fragmento de código se explicaría por sí mismo. @AjayBhojak gracias por abogar ;)