Nehmen Sie ein Beispiel für eine Abstimmungs-DApp. Ein Benutzer klickt auf eine Abstimmungsschaltfläche, dann wird hinter den Kulissen eine Transaktion in der Blockchain abgebaut, und schließlich teilt die DApp dem Benutzer mit, dass seine Stimme aufgezeichnet wurde.
Jetzt gibt es aus irgendeinem Grund eine Kettenreorganisation (möglicherweise hat der Knoten des Benutzers die Netzwerkverbindung verloren und wiederhergestellt).
Wie kann eine DApp web3.js verwenden, um dies zu erkennen, damit sie überprüfen kann, ob die Transaktion des Benutzers rückgängig gemacht wurde und ob der Benutzer seine Stimme erneut abgeben muss? Löst web3.js ein Ereignis aus, um die DApp zu benachrichtigen? Gibt es Code-Snippets, z. B. auf welches Ereignis zu hören ist und wie? Oder gibt es Bibliotheken mit Anwendungsbeispielen?
Hier ist Code, der eine bestimmte Anzahl von Blöcken wartet und überprüft, ob die Transaktionsquittung noch gültig ist. Wenn ein Fork auftritt und die Wiedergabe fehlschlägt, sollte die Empfangsprüfung fehlschlagen und der Rückruf wird mit dem Fehlersatz aufgerufen.
Ich habe dies nur auf Erfolg und Timeout-Fehler getestet, ich habe es nicht auf einem tatsächlichen Fork der Blockchain getestet, weil ich noch nicht herausgefunden habe, wie ich dies in einem Test-Framework zuverlässig veranlassen kann. Schätzen Sie alle Hinweise, wie das geht.
Gemäß der Frage werden nur web3.js-Aufrufe und keine Bibliotheken verwendet. Ich muss Ihnen sagen, Rückrufe statt Versprechungen zu verwenden, ist sehr schmerzhaft für mich ;-P
Ich habe die Validierung der Transaktion mehrerer RPC-Knoten nicht implementiert, aber es gibt einen Hinweis im Code, wo dies zu tun ist. Wahrscheinlich möchten Sie dazu mindestens Async.join verwenden, was eine externe Bibliothek wäre.
//
// @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();
};
[EDIT 1] - kein Gas ist größer oder gleich, nicht größer ...
Ob es dafür in web3 eine Funktion gibt oder nicht, kann ich nicht sagen. Was ich weiß, ist, dass Geth und Mist eine Transaktionswiedergabe haben. Das bedeutet, dass im Falle einer Reorganisation Transaktionen verarbeitet werden, die während der Reorganisation „verloren gegangen“ sind, sodass der Status theoretisch immer noch derselbe sein sollte.
Derzeit glaube ich nicht, dass es eine Möglichkeit gibt, dies zu tun. Derzeit sagen die Dokumente, dass Sie nur 12 Blöcke warten sollen, um sicherzustellen, dass kein Hard Fork aufgetreten ist, und getCode () verwenden. https://github.com/ethereum/wiki/wiki/JavaScript-API#web3ethcontract
In der Web3-API, Abschnitt Contract Events , heißt es, dass das dem Callback übergebene Objekt ein removed
Feld hat. Wenn Sie auf Ihr Ereignis warten und eine Reorganisation auftritt, sollten Sie durch ein Ereignis benachrichtigt werden, removed
das auf gesetzt ist true
.
Ich habe das nie versucht, aber wenn ich das Dokument richtig verstanden habe, sollte es funktionieren.
Ja, es gibt eine Möglichkeit:
Wenn ein Fork auftritt, ändert sich der Hash des Blocks (oder Zustands), sodass Sie nur den Hash des letzten Blocks (oder Zustands) abrufen müssen, bevor Sie eine Transaktion einreichen. Überwachen Sie dann weiterhin eingehende Block-Hashes, um zu überprüfen, ob die Kette noch gültig ist. Nach 10-20 Bestätigungen können Sie diesen Überwachungsprozess beenden und die Transaktion als dauerhaft gespeichert betrachten.
Vereinfachte Schrittfolge wäre:
eth_sendRawTransaction
tun: eth_blockNumber
, dann eth_getBlockByNumber
und speichern Sie den Hash des Blocks (oder Zustands)eth_sendRawTransaction
eth_sendRawTransaction
Aufruf abgerufen haben. Wenn ein Block angekommen ist, der eine fortlaufende Nummer hat und nicht mit dem Hash des übergeordneten Blocks übereinstimmt, ist ein Fork aufgetreten, und Sie können Ihrem Benutzer eine Nachricht anzeigen.Sie können den Hash von Block oder den Hash von State verwenden, es spielt keine Rolle, beide Werte ändern sich beim Fork-Ereignis. Sie sollten auch bedenken, dass während der Verarbeitung Ihrer Transaktion viele Ketten vorhanden sein können, sodass Sie überprüfen müssen, ob Ihre Transaktion in der längsten Kette gespeichert ist. Dies ist die Grundidee, aber natürlich kann die Implementierung komplexer sein, als ich beschrieben habe.
Web3 bietet eine sehr elegante Möglichkeit, dies zu tun. Ich bin mir nicht sicher, warum die anderen Antworten diese Lösung nicht vorgeschlagen haben - es gibt eine Möglichkeit, auf Kettenreorgs zu hören, die Ereignisse ungültig machen:
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");
});
Aus den web3-Dokumenten :
„changed“ gibt zurück Objekt: Wird bei jedem Ereignis ausgelöst, das aus der Blockchain entfernt wurde. Das Ereignis erhält die zusätzliche Eigenschaft „removed: true“.
NB: Derzeit scheint es nicht möglich zu sein, etwas Ähnliches in EthersJS zu tun; nur in Web3.js.
eth
Paul S
eth
Arthur Stankewitsch
Paul S
Paul S
ddayan
Paul S
Paul S
Gleichmut
Paul S