Tome un ejemplo de una DApp de votación. Un usuario hace clic en un botón de voto, luego, entre bastidores, se extrae una transacción en la cadena de bloques y, finalmente, la DApp le dice al usuario que su voto ha sido registrado.
Ahora, por alguna razón, hay una reorganización de la cadena (tal vez el nodo del usuario perdió y recuperó la conectividad de la red).
¿Cómo puede una DApp usar web3.js para detectar esto, de modo que pueda verificar si la transacción del usuario se ha deshecho y si el usuario necesita enviar su voto nuevamente? ¿Web3.js activa un evento para notificar a la DApp? ¿Hay algún fragmento de código, como qué evento escuchar y cómo? ¿O hay alguna biblioteca con ejemplos de su uso?
Aquí hay un código que espera un número específico de bloques y verifica que el recibo de la transacción aún sea válido. Si se produce una bifurcación y la reproducción falla, la verificación de recibo debería fallar y la devolución de llamada llamará con Error establecido.
Solo probé esto para el éxito y las fallas de tiempo de espera, no lo probé en una bifurcación real de la cadena de bloques, porque aún no he descubierto cómo hacer que eso suceda de manera confiable en un marco de prueba. Agradezco cualquier sugerencia sobre cómo hacerlo.
Según la pregunta, solo usa llamadas web3.js y no bibliotecas. Tengo que decirte que usar callbacks en lugar de promesas es muy doloroso para mí ;-P
No he implementado la validación de la transacción en varios nodos RPC, pero hay una nota en el código sobre dónde hacerlo. Probablemente querrá usar al menos Async.join para hacer eso, que sería una biblioteca externa.
//
// @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] - la falta de gas es mayor o igual, no mayor...
No puedo comentar si hay o no una función para esto en web3. Lo que sí sé es que Geth y Mist tienen reproducción de transacciones. Esto significa que, en caso de una reorganización, procesará las transacciones que se 'perdieron' durante la reorganización, por lo que, en teoría, el estado debería seguir siendo el mismo.
Actualmente no creo que haya una manera de hacer eso. Actualmente, los documentos dicen que solo espere 12 bloques para asegurarse de que no haya ocurrido una bifurcación dura y use getCode (). https://github.com/ethereum/wiki/wiki/JavaScript-API#web3ethcontract
En la API web3, sección contract events , se dice que el objeto dado a la devolución de llamada tiene un removed
campo. Si escucha su evento y se produce una reorganización, debe recibir una notificación mediante un evento en el que removed
se establece en true
.
Nunca probé esto, pero si entendí correctamente el documento, debería funcionar.
Sí, hay una manera de hacerlo:
Cuando ocurre una bifurcación, el hash del bloque (o estado) cambiará, por lo que todo lo que tiene que hacer es obtener el hash del último bloque (o estado) antes de enviar una transacción. Luego, siga monitoreando los hashes de bloque entrantes para verificar que la cadena aún sea válida. Después de 10 a 20 confirmaciones, puede detener este proceso de monitoreo y considerar la transacción como almacenada permanentemente.
La secuencia simplificada de pasos sería:
eth_sendRawTransaction
haz: eth_blockNumber
, luego eth_getBlockByNumber
y almacena el Hash del bloque (o Estado)eth_sendRawTransaction
eth_sendRawTransaction
llamada. Si ha llegado un bloque, que tiene un número consecutivo y no coincide con el hash del bloque principal, entonces se ha producido una bifurcación y puede mostrarle un mensaje a su usuario.Puede usar el hash de Block o el hash de State, no importa, ambos valores cambian con el evento de bifurcación. También debe considerar que podría haber muchas cadenas mientras se procesa su transacción, por lo que deberá verificar que su transacción esté almacenada en la cadena más larga. Esta es la idea básica, pero por supuesto, la implementación puede ser más compleja de lo que describí.
Web3 proporciona una manera de hacer esto muy elegante. No estoy seguro de por qué las otras respuestas no han sugerido esta solución: hay una manera de escuchar las reorganizaciones en cadena que invalidan los eventos:
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");
});
De los documentos web3 :
"cambiado" devuelve Objeto: se activa en cada evento que se eliminó de la cadena de bloques. El evento tendrá la propiedad adicional "removida: verdadero".
NB: a partir de ahora parece que no es posible hacer algo similar en EthersJS; solo en Web3.js.
ética
pablo s
ética
Arturo Stankevich
pablo s
pablo s
ddayan
pablo s
pablo s
Gleichmut
pablo s