Я имею дело с очень простым смарт-контрактом:
contract MyRegistry {
struct MyEvent {
string code; // service code, e.g. 1001/FOO/BAR
string desc; // long string
uint count;
bool recorded;
}
event Record(bytes32 hash, string desc, uint count);
function record(bytes32 hash, string code, string desc, uint count) external {
if (registry[hash].recorded)
throw;
registry[hash] = MyEvent(code, desc, count, true);
}
mapping (bytes32 => MyEvent) public registry;
}
Затем я использую web3 getData()
для создания полезной нагрузки транзакции:
var EthTX = require('ethereumjs-tx')
[...]
var calldata = Registry.record.getData(hash, code, desc, count)
[...]
var transaction = new EthTX({
to: registry_address,
gasLimit: 500000,
gasPrice: +web3.toWei(10, 'gwei'),
nonce: myNonce,
data: calldata,
})
Транзакция проходит нормально, но потом, запрашивая registry
через Remix, вижу:
code: ""
desc: ""
count: 27
recorded: true
Струны не записаны!
Я также пытался использовать getData
таким образом:
getData(hash, Web3.fromAscii(code), Web3.fromAscii(desc), count)
и по-другому:
getData(hash, Web3.toHex(code), Web3.toHex(desc), count)
но результат еще хуже:
code: ""
desc: "<long hex string containing both code and desc strings>"
count: 27
recorded: true
Я не могу понять, что происходит. Это похоже на то, что web3 не может правильно сериализовать полезную нагрузку, и, как следствие, интерфейс смарт-контракта не может поместить нужное количество байтов в нужные «слоты».
Есть идеи?
PS если я вызываю record
метод из Ремикса, с теми же значениями, то он явно работает!
Я нашел проблему.
Ошибка действительно в том, что я подозревал: полезная нагрузка была неправильно сериализована, и, как следствие, интерфейс смарт-контракта не мог поместить нужное количество байтов в нужные «слоты».
Причина? Виноват...
hash
является первым параметром метода record
. Кроме того, hash
переменная является результатом JS-библиотеки keccak256, закодированной в шестнадцатеричном формате . Ну, используя getData()
таким образом:
contract.record.getData(hash, code, desc, count)
совершенно неправильно . hash
уже закодирован в шестнадцатеричном формате, но без 0x
префикса. Поэтому getData()
распознайте его как обычную строку и снова преобразуйте в другую шестнадцатеричную строку. Что это значит? это означает, что вновь преобразованная шестнадцатеричная строка больше не вписывается вbytes32
.
Решение: добавьте 0x
префикс, hash
чтобы указать getData()
, что это уже шестнадцатеричная строка (т.е. оставьте ее нетронутой):
contract.record.getData(`0x${hash}`, code, desc, count)
Кроме того, совершенно нет необходимости использовать .fromAscii()
две другие строки, так как getData()
они распознаются как обычные строки, поскольку они не имеют 0x
префикса, и поэтому автоматически преобразуют их в шестнадцатеричный формат.