Не удается правильно сохранить строки в смарт-контракте Solidity с использованием Web3

Я имею дело с очень простым смарт-контрактом:

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метод из Ремикса, с теми же значениями, то он явно работает!

Ответы (1)

Я нашел проблему.

Ошибка действительно в том, что я подозревал: полезная нагрузка была неправильно сериализована, и, как следствие, интерфейс смарт-контракта не мог поместить нужное количество байтов в нужные «слоты».

Причина? Виноват...

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префикса, и поэтому автоматически преобразуют их в шестнадцатеричный формат.