Двоичные данные журнала программы
Проблема
В Solidity нет поддержки регистрации двоичных данных.
События в Solidity
В Solidity можно сообщать о событиях. Они выглядят как структуры с нулем или более полей и могут быть испущены с определенными значениями. Например:
event PaymentReceived {
address sender;
uint amount;
}
contract c {
function pay() public payable {
emit PaymentReceived(msg.sender, msg.value);
}
}
События доступны только для записи с точки зрения Solidity/VM и записываются в блоки в записях tx.
Некоторые из этих полей могут быть помечены как «индексированные», что влияет на способ кодирования данных. Все неиндексированные поля кодируются eth abi в массив байтов переменной длины. Все проиндексированные поля входят в так называемые темы.
Темы представляют собой поля фиксированной длины в 32 байта. Максимум 4 темы; если тип не всегда умещается в 32 байта (например, строковые типы), то топик хешируется keccak256.
Первая тема — это хэш keccak256 подписи события, в данном случае keccak256('PaymentReceived(address,uint)')
. Четыре оставшихся доступны для «индексированных» полей. Событие может быть объявлено «анонимным», и в этом случае первое поле не является хэшем подписи, и разрешено иметь 4 проиндексированных поля.
Прослушивание событий в клиенте
Причина различия между темами/индексируемыми и обычными полями заключается в том, что их легче фильтровать по темам.
const Web3 = require('web3');
const url = 'ws://127.0.0.1:8546';
const web3 = new Web3(url);
var options = {
address: '0xfbBE8f06FAda977Ea1E177da391C370EFbEE3D25',
topics: [
'0xdf50c7bb3b25f812aedef81bc334454040e7b27e27de95a79451d663013b7e17',
//'0x0000000000000000000000000d8a3f5e71560982fb0eb5959ecf84412be6ae3e'
]
};
var subscription = web3.eth.subscribe('logs', options, function(error, result){
if (!error) console.log('got result');
else console.log(error);
}).on("data", function(log){
console.log('got data', log);
}).on("changed", function(log){
console.log('changed');
});
Для декодирования неиндексированных полей (данных) необходим abi контракта. Таким образом, тема сначала используется для обнаружения того, какое событие было использовано, а затем данные могут быть декодированы.
Ethereum Tx в блоке
Транзакция вызывает журналы событий. Вот tx с одним событием, с 3 темами и некоторыми данными.
{
"tx": {
"nonce": "0x2",
"gasPrice": "0xf224d4a00",
"gas": "0xc350",
"to": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"value": "0x0",
"input": "0xa9059cbb000000000000000000000000a12431d0b9db640034b0cdfceef9cce161e62be40000000000000000000000000000000000000000000000a030dcebbd2f4c0000",
"hash": "0x98a67f0a35ebc0ac068acf0885d38419c632ffa4354e96641d6d5103a7681910",
"blockNumber": "0xc96431",
"from": "0x82f890D638478d211eF2208f3c1466B5Abf83551",
"transactionIndex": "0xe1"
},
"receipt": {
"gasUsed": "0x74d2",
"status": "0x1",
"logs": [
{
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x00000000000000000000000082f890d638478d211ef2208f3c1466b5abf83551",
"0x000000000000000000000000a12431d0b9db640034b0cdfceef9cce161e62be4"
],
"data": "0x0000000000000000000000000000000000000000000000a030dcebbd2f4c0000"
}
]
}
}
Дополнительные соображения
В Ethereum события хранятся в блоках. События отмечают определенные изменения состояния в смарт-контрактах. Это служит двум целям:
- Слушайте события (т. е. изменения состояния), когда они происходят, читая новые блоки по мере их публикации.
- Перечитывайте исторические события, читая старые блоки
Так, например, смарт-контракты могут выдавать изменения по мере их возникновения, но никогда не выдавать полное состояние, поэтому единственный способ восстановить все состояние контракта — это повторно прочитать все события из цепочки. Таким образом, приложение будет считывать события из блока 1 или любого другого блока, в котором было развернуто приложение, а затем использовать это состояние для локальной обработки. Это локальный кеш, который может быть повторно заполнен из цепочки в любой момент.
Предложенное решение
Двоичное ведение журнала должно быть добавлено в журнал программы. Журнал программы должен включать данные в кодировке base64 (разрешено ноль или более единиц).
Итак, если мы сначала закодируем темы, а затем данные, то событие в tx выше будет выглядеть так:
program data: 3fJSrRviyJtpwrBo/DeNqpUrpFjxKEWKPVaTfUjs8AAAAAAAAAAAAAAACC+JDWOEeNIR7yII88FGa1q/g1UQAAAAAAAAAAAAAAAKEkMdC522QANLDN/O75zOFh5ivk AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgMNzrvS9MAAA=
Для этого требуется новый системный вызов:
void sol_log_data(SolBytes *fields, uint64_t length);
Соображения
- Должно ли быть текстовое поле в журнале программы, чтобы мы могли иметь немного метаданных в двоичных данных, чтобы сделать их более удобочитаемыми для человека.