Here's code that waits specified number of blocks and verifies the transaction receipt is still valid. If a fork occurs and the replay fails, the receipt check should fail and the callback will call with Error set.
I've only tested this for success and timeout failures, I've not tested it on an actual fork of the blockchain, because I haven't figured out how to reliably cause that to happen yet in a test framework. Appreciate any hints on how to do that.
Per the question, it only uses web3.js calls, and no libraries. I have to tell you using callbacks instead of promises is very painful for me ;-P
I haven't implemented validating the transaction multiple RPC nodes, but there's a note in the code on where to do that. You will probably want to use at least Async.join to do that, which would be an external library.
//
// @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] - out of gas is greater than or equal, not greater...
You could certainly make a daemon that listens for the event and calls the contract. As you describe it it doesn't even need to be a web service; For example, just write the code that you'd normally have run in the browser to listen for the event then fire the contract, and run it from the command line with node. You may want to run this with upstart or god that will start it up again if it falls over for some reason.
However, is there a reason why you can't just make the contract that's currently calling the original event call the second contract directly?
Best Answer
You can try
web3.net.listening
This property is read only and says whether the node is actively listening for network connections or not.
Returns
Boolean -
true
if the client is actively listening for network connections, otherwisefalse
.