Создание контракта с конструктором в ethereumj

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

Если выше приведен исходный код контракта, который я создал, то как вызвать функцию калькулятора с помощью API ethereumj. я уже использовал

contract.callFunction("Calculator", 10)

Но это не сработало. Также называемый

contract.callConstFunction("Calculator", 10)

Но это тоже не сработало.

И когда я увидел ABI, у него был тип функции = "конструктор", но он не смог найти ни одного метода, представляющего вызывающий конструктор.

Пожалуйста, предложите, есть ли способ вызвать функции конструктора?

Ответы (3)

Я наконец нашел решение. Как обсуждалось в другом ответе, вам необходимо проанализировать и добавить параметры инициализации к коду контракта перед отправкой транзакции. На самом деле я не нашел элегантного и универсального способа анализа аргументов непосредственно в Java (но это нетрудно сделать), поэтому я декодировал синтаксис ABI в byte[].

Прежде всего скопируйте StandaloneBlockchain.java и добавьте один метод (извините, я не могу найти простой способ сделать это через расширение):

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;
}

Затем в StandaloneBlockchainSample.java сослаться на модифицированный класс, изменить типовой контракт, чтобы в конструкторе было 2 параметра (у меня не было времени проверить все типы, но должно работать без проблем) и присвоить параметрам начальные значения:

// 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...");

После этого проверьте правильность значения параметров с помощью:

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

Обратите внимание, что вам нужно изменить частный метод assertEqual внутри класса, чтобы утверждать другие типы, кроме уже определенного BigInteger. Я протестировал это решение с EthereumJ 1.2.0: возможно, в следующих выпусках будет лучший способ передачи значений инициализации в конструктор, поэтому обновите ответ, если вы знаете об этом.

Вам не нужно вызывать конструктор, вам нужно отправить контракт, и у вас автоматически будет указатель на контракт, таким образом:

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

Вы можете увидеть простой, но полностью рабочий пример на github .

Я использовал этот пример и знаю, что он работает без конструктора. Но есть необходимость использовать солидный контракт с конструктором, посмотрите пример Crowdsale или создайте свою собственную криптовалюту на ethereum.org. Если вы заказываете кошелек Mist, он позволяет вам создавать контракты с конструктором, поэтому ваш ответ не решает мою проблему. Все еще нужен кто-то, чтобы сказать, как вызвать конструктор контракта, который я создал. Спасибо, что нашли время ответить.
Возможно, я неправильно понял ваш вопрос. В вашем примере Calculator является конструктором, потому что он имеет то же имя, что и контракт. Насколько мне известно, вы не можете вызывать конструкторы извне EVM (т.е. из клиента в Java, Web3 и т. д.), а изнутри с помощью инструкций типа «Calculator calc = new Calculator()» внутри метода контракта. Конструктор вызывается на этапе создания при развертывании контракта. Вы можете ссылаться на развернутый контракт для вызова методов. Все эти вызовы методов затем преобразуются клиентом (Java в вашем случае) в транзакции для отправки в сеть.
Ага. Вы, может быть, правы. Но тогда должен быть какой-то способ передать аргументы при отправке конструктора. Это должно было быть там, но удивительно, что его нет, и самые первые примеры на ethereum.org объясняют использование конструкторов. И, как вы предложили «Calculator calc = new Calculator()», это не кажется правильным, потому что тогда вам нужно жестко закодировать аргументы, что само по себе не является хорошей практикой. Еще одна вещь заключается в том, что когда вы проверяете ABI содержит аргумент и тип функции в качестве конструктора, но не имя конструктора. Спасибо
Теперь я вижу. Проблема заключается в том, как передать начальные значения на этапе развертывания контракта. В Web3 это действительно просто (т.е. MyContract.new(param1, param2, ...)) но в EthereumJ я не вижу простого способа сделать это. Под капотом, чтобы передать значение инициализации конструктору, вам нужно «кодировать ABI» эти параметры и добавить байты к байтам контракта, чтобы байты данных транзакции содержали код + параметры. В частности, все должно быть помещено в массив байтов org.ethereum.core.Transaction.data перед отправкой. Я буду копать дальше, это кажется очень полезным дополнением, чтобы иметь простой способ передачи параметров
Спасибо. Я также рассмотрю это. Также постараюсь предложить больше изменений.

Добавлено в 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);
Что было добавлено? Не могли бы вы объяснить, о чем ваш ответ?
@ 5chdn в потоке разработки ethereumj был поднят вопрос о возможности прямого вызова конструктора с начальными аргументами, поэтому исправление для этого добавлено в поток, я еще не тестировал его, Антон упомянул об изменениях для того же, так что я думаю, он заслуживает плюсов, а не минусов. :)
Просто подумал, что фрагмент кода будет говорить сам за себя. @AjayBhojak спасибо за поддержку;)