Как вызвать метод контракта с помощью API eth_call JSON-RPC

Итак, я пытаюсь вызвать метод контракта, используя методы, предоставляемые интерфейсом Ethereum JSON RPC. JSON RPC работает на компьютере с Ubuntu. К сожалению, я не могу получить результат от тестового контракта, который я создал.

Первое, что я сделал, это запустил Go Ethereum в тестовой сети с помощью:

geth --rpc --testnet

Затем я открываю другую консоль и открываю консоль JS с помощью

geth attach

Затем я скомпилировал свой контракт с помощью Browser Solidity, где мой код:

 contract test { function double(int a) returns(int) {   return 2*a;   } }

Что дает мне следующий байтовый код:

606060405260728060106000396000f360606040526000357c0100000000000000000000000000000000000000000000000000000000900480636ffa1caa146037576035565b005b604b60048080359060200190919050506061565b6040518082815260200191505060405180910390f35b6000816002029050606d565b91905056

Затем я создал контракт с:

curl localhost:8545 -X POST --data '{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{"from": "0x8aff0a12f3e8d55cc718d36f84e002c335df2f4a", "data": "606060405260728060106000396000f360606040526000357c0100000000000000000000000000000000000000000000000000000000900480636ffa1caa146037576035565b005b604b60048080359060200190919050506061565b6040518082815260200191505060405180910390f35b6000816002029050606d565b91905056"}],"id":1}

Что возвращает мне хэш транзакции, который я использую для получения адреса контракта:

curl localhost:8545 -X POST --data '{"jsonrpc":"2.0","method":"eth_getTransactionReceipt","params":["0x8290c22bd9b4d61bc57222698799edd7bbc8df5214be44e239a95f679249c59c"],"id":1}'

Что возвращает мне следующее:

{"id":1,"jsonrpc":"2.0","result":{"transactionHash":"0x8290c22bd9b4d61bc57222698799edd7bbc8df5214be44e239a95f679249c59c","transactionIndex":"0x0","blockNumber":"0xd32da","blockHash":"0xf13f185f0eb1e4797885400e3b371c972eedebcf3eef27815a45b649283ec669","cumulativeGasUsed":"0x14293","gasUsed":"0x14293","contractAddress":"0x5c7687810ce3eae6cda44d0e6c896245cd4f97c6","logs":[]}}

Затем я хотел назвать метод «двойным» с номером «5». В документации Contract ABI сказано, что вы должны взять первые 4 байта хэша Keccak.
Сигнатура метода

double(int)

Что дает хэш с помощью web3.sha3("double(int)":

6740d36c7d42fba12b8eb3b047193c9761224c267d7a2e96dc50949860652317

Первые четыре байта с префиксом «0x»:

0x6740d36c

Затем документация говорит взять параметр, закодировать его в шестнадцатеричном формате и дополнить его слева до 32 байтов. Что будет следующим, используя число «5»:

0x0000000000000000000000000000000000000000000000000000000000000005

В итоге закодированная строка должна выглядеть так:

0x6740d36c0000000000000000000000000000000000000000000000000000000000000005

Итак, используя это для вызова метода:

 curl localhost:8545 -X POST --data '{"jsonrpc":"2.0","method":"eth_call","params":[{"from": "0x8aff0a12f3e8d55cc718d36f84e002c335df2f4a", "to": "0x5c7687810ce3eae6cda44d0e6c896245cd4f97c6", "data": "0x6740d36c0000000000000000000000000000000000000000000000000000000000000005"}, "latest"],"id":1}'

Что дает мне этот результат:

{"id":1,"jsonrpc":"2.0","result":"0x"}

Я делаю что-то не так или забыл важный шаг?

Кроме того, есть ли какой-либо способ получить доступ к уже существующим методам в библиотеке Go Ethereum, который освободил бы меня от необходимости реализовывать, например, методы для заполнения самостоятельно?

Заранее большое спасибо!

Кто-нибудь развернул этот контракт в основной сети, чтобы мы могли следить за ним?
Я использую JSON RPC через С#. Итак, мне действительно нужно увидеть эквивалент в Json. Есть ли где-нибудь, где я могу это увидеть, но с Json?

Ответы (3)

Резюме

  • Ваш исходный код контракта не будет работать правильно, пока я не добавлю constantв определение функции, чтобы указать, что эта функция не изменяет блокчейн.

  • Мне пришлось использовать сигнатуру метода double(int256)вместо того double(int), чтобы заставить eth_callJSON-RPC работать.



Детали

Запустите свой gethэкземпляр

Вы можете использовать блокчейн Testnet , который синхронизируется с другими узлами через Интернет, или вы можете использовать частный блокчейн Dev , который доступен только на вашем компьютере и быстрее разрабатывается. Параметр --rpcпозволит вам использовать curlдля связи с вашим gethэкземпляром.

Блокчейн тестовой сети

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

geth --testnet account new
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase: 
Repeat Passphrase: 
Address: {aaaa725f2f36a28c01bc47f883534990c9c8bbbb}

Ваш gethэкземпляр должен будет синхронизировать блокчейн Testnet с другими узлами в Интернете. Это может занять и час или больше. После синхронизации ваша операция майнинга начнется, и вы должны добыть несколько монет через 10 или 20 минут. Начните gethс параметров майнинга:

geth --testnet --rpc --mine --minerthreads 1 console

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

> web3.fromWei(eth.getBalance(eth.accounts[0]), "ether");
25

Если вы хотите выйти из geth, нажмите Control-D.

Если вы хотите, теперь вы можете запустить свой gethэкземпляр без параметров майнинга, так как другой gethэкземпляр где-то в Интернете будет майнить ваши транзакции. Используйте следующую команду, чтобы удалить опцию майнинга, или просто используйте предыдущую gethкоманду:

geth --testnet --rpc console

Блокчейн для разработчиков

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

geth --dev account new
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase: 
Repeat Passphrase: 
Address: {cccc725f2f36a28c01bc47f883534990c9c8dddd}

Начните gethс параметров --minerи --minerthreads, поскольку вашему gethэкземпляру потребуется майнить вашу частную цепочку блоков:

geth --dev --rpc --mine --minerthreads 1 console


Сгладьте свой код и назначьте его переменной

Мне пришлось изменить ваш код, чтобы он работал правильно, добавив слово constantв определение вашей функции, поскольку эта функция не изменяет блокчейн при запуске.

В вашем примере ваш код обычно будет отформатирован для удобства чтения:

contract Test { 
    function double(int a) constant returns(int) {
        return 2*a;
    } 
}

Вы можете использовать такой сервис, как Line Break Removal Tool , чтобы удалить разрывы строк, или посмотреть, как загрузить исходный файл Solidity в geth для некоторых альтернатив.

Ваш плоский код будет выглядеть как пример, который вы опубликовали:

contract Test { function double(int a) constant returns(int) { return 2*a; } }

Вам нужно будет присвоить свой код строковой переменной:

var testSource='contract Test {  function double(int a) constant returns(int) { return 2*a; } }'

Обратите внимание, что //комментарии вызовут некоторые проблемы, если ваш код будет сглажен, так как весь текст после //комментария будет рассматриваться как комментарий. Используйте /* ... */вместо этого, если вам нужно добавить комментарии к вашему коду.


Скомпилируйте свой код

В вашем gethэкземпляре введите сглаженный код с назначением переменной. Не беспокойтесь о undefinedрезультате, так как это нормальный ответ от geth:

> var testSource='contract Test {  function double(int a) constant returns(int) { return 2*a; } }'
undefined

Скомпилируйте свой код:

> var testCompiled = web3.eth.compile.solidity(testSource);
I0503 09:04:15.907715    3190 solidity.go:114] solc, the solidity compiler commandline interface
Version: 0.3.2-0/Release-Linux/g++/Interpreter

path: /usr/bin/solc
undefined

Если вы хотите увидеть двоичную форму вашего кода:

testCompiled
{
  Test: {
    code: "0x6060604052602a8060106000396000f3606060405260e060020a60003504636ffa1caa8114601a575b005b6002600435026060908152602090f3",
    info: {
      abiDefinition: [{...}],
      compilerOptions: "--bin --abi --userdoc --devdoc --add-std --optimize -o /tmp/solc497335011",
      compilerVersion: "0.3.2",
      developerDoc: {
        methods: {}
      },
      language: "Solidity",
      languageVersion: "0.3.2",
      source: "contract Test {  function double(int a) constant returns(int) { return 2*a; } }",
      userDoc: {
        methods: {}
      }
    }
  }
}

Вам понадобится подпись Application Binary Interface (ABI) для вашего контракта, если вы хотите получить доступ к запуску вашей функции вне этого gethсеанса:

> testCompiled.Test.info.abiDefinition
[{
    constant: true,
    inputs: [{
        name: "a",
        type: "int256"
    }],
    name: "double",
    outputs: [{
        name: "",
        type: "int256"
    }],
    type: "function"
}]

Примечание . Это вторая подсказка о том, почему ваш вызов JSON RPC не сработал — сигнатура метода имеет int256параметр aвместо int.


Вставьте свой код в блокчейн

Выполните следующие команды, чтобы вставить свой код в блокчейн:

> var testContract = web3.eth.contract(testCompiled.Test.info.abiDefinition);
undefined
> var test = testContract.new({
    from:web3.eth.accounts[0], 
    data: testCompiled.Test.code, gas: 2000000}, 
    function(e, contract) {
      if (!e) {
        if (!contract.address) {
          console.log("Contract transaction send: TransactionHash: " +
            contract.transactionHash + " waiting to be mined...");
        } else {
          console.log("Contract mined! Address: " + contract.address);
          console.log(contract);
        }
    }
})
I0503 09:09:39.632499    3190 xeth.go:1026] Tx(0x8c15e8b8bb593d4a680b084a455ba82b103e60204638a3674ef1fb90ca0ad4f0) created: 0x7a2d8fa11aa28097135eb514f6a23ba12501191e
Contract transaction send: TransactionHash: 0x8c15e8b8bb593d4a680b084a455ba82b103e60204638a3674ef1fb90ca0ad4f0 waiting to be mined...
undefined

Подождите несколько минут, и вы увидите следующий вывод:

I0503 09:10:34.319772    3190 worker.go:569] commit new work on block 11122 with 0 txs & 0 uncles. Took 431.081µs
Contract mined! Address: 0x7a2d8fa11aa28097135eb514f6a23ba12501191e
[object Object]

Вам следует сохранить адрес, если вы хотите запустить этот контракт вне gethсеанса.


Используйте свой контракт в рамках одной gethсессии

Прежде чем добавить в constantфункцию double, я получил следующие результаты:

> test.double(5)
invalid address

Примечание . Это первая подсказка о том, почему ваш вызов JSON RPC не удался.

Чтобы использовать ваш контракт в gethсеансе, ПОСЛЕ добавления constantв doubleфункцию:

> test.double(5)
10
> test.double(55)
110


Используйте свой контракт с использованием curlи JSON-RPC

А теперь мы подошли к сложной части.

Ваша функция double(int)действительно double(int256). Из Solidity - Типы :

int• / uint•: Целые числа со знаком и без знака различных размеров. Ключевые слова от uint8 до uint256 с шагом 8 (от 8 до 256 бит без знака) и от int8 до int256. uint и int являются псевдонимами для uint256 и int256 соответственно.

В geth, мы запускаем следующую команду, чтобы найти сигнатуру double(...)функции:

> web3.sha3('double(int256)')
"6ffa1caacdbca40c71e3787a33872771f2864c218eaf6f1b2f862d9323ba1640"

И возьмите первые 4 байта для создания dataпараметра

0x6ffa1caa0000000000000000000000000000000000000000000000000000000000000005

И используя адрес из параметра «Contract mined!», строим следующую curlкоманду:

curl localhost:8545 -X POST --data '{"jsonrpc":"2.0", "method":"eth_call", "params":[{"from": "eth.accounts[0]", "to": "0x65da172d668fbaeb1f60e206204c2327400665fd", "data": "0x6ffa1caa0000000000000000000000000000000000000000000000000000000000000005"}], "id":1}'

И запустите эту команду из командной строки:

user@Kumquat:~$ curl localhost:8545 -X POST --data '{"jsonrpc":"2.0", "method":"eth_call", "params":[{"from": "eth.accounts[0]", "to": "0x65da172d668fbaeb1f60e206204c2327400665fd", "data": "0x6ffa1caa0000000000000000000000000000000000000000000000000000000000000005"}], "id":1}'
{"id":1,"jsonrpc":"2.0","result":"0x000000000000000000000000000000000000000000000000000000000000000a"}

Результатом 0x0000...000aявляется значение 10.


Обновление от 18 октября 2016 г.

Как описано в ошибке RPC «недопустимое или отсутствующее значение для параметров» при вызове функций void константы контракта , вам может потребоваться добавить параметр блока в paramsсписок. Ваша команда с параметром блока будет:

user@Kumquat:~$ curl localhost:8545 -X POST --data '{"jsonrpc":"2.0", "method":"eth_call", "params":[{"from": "eth.accounts[0]", "to": "0x65da172d668fbaeb1f60e206204c2327400665fd", "data": "0x6ffa1caa0000000000000000000000000000000000000000000000000000000000000005"}, "latest"], "id":1}'
{"id":1,"jsonrpc":"2.0","result":"0x000000000000000000000000000000000000000000000000000000000000000a"}

Возможно, вам также придется пропустить fromпараметр.

Использованная литература:


Ваш другой вопрос

Кроме того, есть ли какой-либо способ получить доступ к уже существующим методам в библиотеке Go Ethereum, который освободил бы меня от необходимости реализовывать, например, методы для заполнения самостоятельно?

Не могли бы вы задать этот вопрос в другом вопросе, так как этот ответ уже слишком длинный.

Сложность заключалась в предоставлении данных. Я застрял в получении dataмоей функции на целый день, даже после такого отличного объяснения. Но do we really need to do all this for providing data? Позже я наткнулся на getData(..)то, что сделал все это за меня и не нуждался в каких-либо сложных шагах. Просто var callData=myContract.myFunction.getData(parameters)и я получил data.
Также просто к сведению, имя функции «двойное», так что это не тип внутри типа.

Чтобы использовать eth_call, сложная часть заключается в предоставлении data, который представляет собой ABI , кодирующий функцию, которую вы хотите вызвать, и ее аргументы.

Важно использовать канонические типы , поэтому double(int256)вместо double(int).

> web3.sha3('double(int256)').substr(0, 8)
"6ffa1caa"

Заполнение и динамические типы должны быть указаны правильно. В этом примере int256аргумент 5просто дополняется:

0000000000000000000000000000000000000000000000000000000000000005

Объединение вышеизложенного дает:

0x6ffa1caa000000000000000000000000000000000000000000000000000000000000000005

что фактически означает double(5), и указать его как data:

curl localhost:8545 -X POST --data '{"jsonrpc":"2.0", "method":"eth_call", "params":[{"from": "0xsenderAccount", "to": "0xcontractAddress", "data": "0x6ffa1caa000000000000000000000000000000000000000000000000000000000000000005"}], "id":1}'

Это toадрес вызываемого контракта и fromучетная запись отправителя (обычно eth.accounts[0]).


РЕДАКТИРОВАТЬ: Дополнительная информация о «сложной части предоставления data»:

web3.js можно настроить и getDataиспользовать следующим образом:

var myCallData = myContractInstance.myMethod.getData(param1 [, param2, ...]);

(В этом случае можно также рассмотреть возможность вызова web3.js myMethodвместо использования eth_callJSON-RPC.)

что, если функция исключает более одного параметра, например функции, которые принимают адрес и uint, как получить data?
@PrashantPrabhakarSingh Некоторые ссылки ethereum.stackexchange.com/questions/7675/… которые ведут на ethereum.stackexchange.com/questions/3364/… РЕДАКТИРОВАТЬ: Видел, что вы нашли web3.js getDataв своем комментарии , который работает (но поскольку это включает в себя настройку web3 .js, проще всего позволить web3.js также вызывать функцию вместо RPC eth_call).

Рассмотрите возможность разблокировки учетной записи перед отправкой транзакции:

personal.unlockAccount(eth.accounts[0], 'your password goes here', 9000)

или вызов RPC:

curl your_ethereum_node_address:port -X POST --data '{"jsonrpc":"2.0","method":"personal_unlockAccount","params": ["your account address","password of your account",null],"id":1}'

Я довольно долго пытался выяснить, почему контракт не был в очереди.

См. пункт 3 в разделе Как снизить вероятность взлома вашего кошелька Ethereum? . По крайней мере, в старых версиях geth ваш пароль хранится в текстовом файле истории.
как я могу разблокировать учетную запись, используя закрытый ключ вместо пароля учетной записи, и выполнить транзакцию @michail
unlockAccountустарел и не работает в 2019 году, вместо этого используйте private_key и сделайте подпись, а затем send_raw_transaction (или аналогичный метод)