Итак, я пытаюсь вызвать метод контракта, используя методы, предоставляемые интерфейсом 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, который освободил бы меня от необходимости реализовывать, например, методы для заполнения самостоятельно?
Заранее большое спасибо!
Ваш исходный код контракта не будет работать правильно, пока я не добавлю constant
в определение функции, чтобы указать, что эта функция не изменяет блокчейн.
Мне пришлось использовать сигнатуру метода double(int256)
вместо того double(int)
, чтобы заставить eth_call
JSON-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
.
Как описано в ошибке 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
параметр.
Использованная литература:
eth_call
Кроме того, есть ли какой-либо способ получить доступ к уже существующим методам в библиотеке 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_call
JSON-RPC.)
data
?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}'
Я довольно долго пытался выяснить, почему контракт не был в очереди.
unlockAccount
устарел и не работает в 2019 году, вместо этого используйте private_key и сделайте подпись, а затем send_raw_transaction (или аналогичный метод)
Уильям Энтрикен
Кристиан Финдли