Возьмем пример DApp для голосования. Пользователь нажимает кнопку голосования, затем за кулисами транзакция обрабатывается в блокчейне, и, наконец, DApp сообщает пользователю, что его голос был записан.
Теперь по какой-то причине происходит реорганизация цепочки (возможно, нода пользователя потеряла и восстановила подключение к сети).
Как DApp может использовать web3.js для обнаружения этого, чтобы он мог проверить, была ли транзакция пользователя отменена и нужно ли пользователю снова отправить свой голос? Запускает ли web3.js событие для уведомления DApp? Есть ли фрагменты кода, например, какое событие прослушивать и как? Или есть библиотеки с примерами их использования?
Вот код, который ожидает указанное количество блоков и проверяет, что квитанция транзакции все еще действительна. Если происходит разветвление и воспроизведение не удается, проверка получения должна завершиться неудачно, и обратный вызов будет выполнен с установленным значением Error.
Я проверял это только на успех и сбои тайм-аута, я не тестировал его на фактическом форке блокчейна, потому что я еще не понял, как надежно заставить это произойти в тестовой среде. Цените любые подсказки о том, как это сделать.
Согласно вопросу, он использует только вызовы web3.js и не использует библиотеки. Я должен сказать вам, что использование обратных вызовов вместо промисов для меня очень болезненно ;-P
Я не реализовал проверку транзакции на нескольких узлах RPC, но в коде есть примечание о том, где это сделать. Вы, вероятно, захотите использовать для этого хотя бы Async.join, который будет внешней библиотекой.
//
// @method awaitBlockConsensus
// @param web3s[0] is the node you submitted the transaction to, the other web3s
// are for cross verification, because you shouldn't trust one node.
// @param txhash is the transaction hash from when you submitted the transaction
// @param blockCount is the number of blocks to wait for.
// @param timout in seconds
// @param callback - callback(error, transaction_receipt)
//
exports.awaitBlockConsensus = function(web3s, txhash, blockCount, timeout, callback) {
var txWeb3 = web3s[0];
var startBlock = Number.MAX_SAFE_INTEGER;
var interval;
var stateEnum = { start: 1, mined: 2, awaited: 3, confirmed: 4, unconfirmed: 5 };
var savedTxInfo;
var attempts = 0;
var pollState = stateEnum.start;
var poll = function() {
if (pollState === stateEnum.start) {
txWeb3.eth.getTransaction(txhash, function(e, txInfo) {
if (e || txInfo == null) {
return; // XXX silently drop errors
}
if (txInfo.blockHash != null) {
startBlock = txInfo.blockNumber;
savedTxInfo = txInfo;
console.log("mined");
pollState = stateEnum.mined;
}
});
}
else if (pollState == stateEnum.mined) {
txWeb3.eth.getBlockNumber(function (e, blockNum) {
if (e) {
return; // XXX silently drop errors
}
console.log("blockNum: ", blockNum);
if (blockNum >= (blockCount + startBlock)) {
pollState = stateEnum.awaited;
}
});
}
else if (pollState == stateEnum.awaited) {
txWeb3.eth.getTransactionReceipt(txhash, function(e, receipt) {
if (e || receipt == null) {
return; // XXX silently drop errors. TBD callback error?
}
// confirm we didn't run out of gas
// XXX this is where we should be checking a plurality of nodes. TBD
clearInterval(interval);
if (receipt.gasUsed >= savedTxInfo.gas) {
pollState = stateEnum.unconfirmed;
callback(new Error("we ran out of gas, not confirmed!"), null);
} else {
pollState = stateEnum.confirmed;
callback(null, receipt);
}
});
} else {
throw(new Error("We should never get here, illegal state: " + pollState));
}
// note assuming poll interval is 1 second
attempts++;
if (attempts > timeout) {
clearInterval(interval);
pollState = stateEnum.unconfirmed;
callback(new Error("Timed out, not confirmed"), null);
}
};
interval = setInterval(poll, 1000);
poll();
};
[РЕДАКТИРОВАТЬ 1] - из газа больше или равно, не больше...
Я не могу комментировать, есть ли такая функция в web3. Что я знаю точно, так это то, что у Geth и Mist есть повтор транзакций. Это означает, что в случае реорганизации он будет обрабатывать транзакции, которые были «потеряны» во время реорганизации, поэтому теоретически состояние должно оставаться прежним.
В настоящее время я не думаю, что есть способ сделать это. В настоящее время в документах говорится, что нужно просто подождать 12 блоков, чтобы убедиться, что хард-форк не произошел, и использовать getCode(). https://github.com/ethereum/wiki/wiki/JavaScript-API#web3ethcontract
В web3 API, в разделе Contract Events, сказано, что объект, передаваемый обратному вызову, имеет removed
поле. Если вы прослушиваете свое событие и происходит реорганизация, вы должны быть уведомлены событием, в котором removed
установлено значение true
.
Я никогда не пробовал это, но если правильно понял документ, он должен работать.
Да, есть способ сделать это:
Когда происходит форк, хэш блока (или состояния) изменится, поэтому все, что вам нужно сделать, это получить хеш последнего блока (или состояния) перед отправкой транзакции. Затем продолжайте отслеживать хэши входящих блоков, чтобы убедиться, что цепочка все еще действительна. После 10-20 подтверждений вы можете остановить этот процесс мониторинга и считать транзакцию постоянно хранимой.
Упрощенная последовательность шагов будет такой:
eth_sendRawTransaction
do: eth_blockNumber
, затем eth_getBlockByNumber
сохраните хэш блока (или состояния)eth_sendRawTransaction
eth_sendRawTransaction
вызовом. Если прибыл блок, который имеет последовательный номер и не совпадает с хэшем родительского блока, то произошло разветвление, и вы можете показать своему Пользователю сообщение.Вы можете использовать хэш блока или хэш состояния, это не имеет значения, оба значения меняются при возникновении форка. Вы также должны учитывать, что во время обработки вашей транзакции может быть много цепочек, поэтому вам нужно будет проверить, хранится ли ваша транзакция в самой длинной цепочке. Это основная идея, но, конечно, реализация может быть сложнее, чем я описал.
Web3 позволяет сделать это очень элегантно. Я не уверен, почему другие ответы не предложили это решение - есть способ прослушать реорганизацию цепочки, аннулирующую события:
myContract.events.MyVoteEvent()
.on("data", async (error, event) => {
console.log("vote received");
})
.on("changed", async (error, event) => { // Called when event is no longer valid
console.log("vote was invalidated due to reorg");
});
Из документов web3 :
«changed» возвращает Object: срабатывает при каждом событии, которое было удалено из блокчейна. Событие будет иметь дополнительное свойство «removed: true».
NB: на данный момент кажется, что сделать подобное в EthersJS невозможно; только в Web3.js.
эт
Пол С
эт
Артур Станкевич
Пол С
Пол С
ддаян
Пол С
Пол С
Гляйхмут
Пол С