Poll chain state for contract callback invocation

Previously we relied on filters to watch for block events for firing contract-
creation callback functions. With this change, we now poll the chain's state on
the javascript side using `setInterval`.

This is necessary for low-latency consensus mechanisms like Raft, where a block
event reguarly fires before the web3 layer is able to set the filter to watch
for such events.

This fixes #86.
This commit is contained in:
Brian Schroeder 2017-04-06 13:30:59 -04:00
parent d442011414
commit 370b9024ec
1 changed files with 49 additions and 46 deletions

View File

@ -2897,27 +2897,37 @@ var addEventsToContract = function (contract) {
*/ */
var checkForContractAddress = function(contract, callback){ var checkForContractAddress = function(contract, callback){
var count = 0, var count = 0,
callbackFired = false; callbackFired = false,
filter = null,
interval = null,
stop = function() {
if (interval) { clearInterval(interval); };
if (filter) { filter.stopWatching(); };
callbackFired = true;
};
// wait for receipt // Track the number of blocks that are added to the chain so that we can
var filter = contract._eth.filter('latest', function(e){ // timeout.
filter = contract._eth.filter('latest', function(e){
if (!e && !callbackFired) { if (!e && !callbackFired) {
count++; count++;
// stop watching after 50 blocks (timeout)
if (count > 50) { if (count > 50) {
stop();
filter.stopWatching();
callbackFired = true;
if (callback) if (callback)
callback(new Error('Contract transaction couldn\'t be found after 50 blocks')); callback(new Error('Contract transaction couldn\'t be found after 50 blocks'));
else else
throw new Error('Contract transaction couldn\'t be found after 50 blocks'); throw new Error('Contract transaction couldn\'t be found after 50 blocks');
}
}
});
// If we want to guarantee the firing of our callback, using a filter
} else { // alone won't suffice, because there is always the possibility that our
// block event already fired in high-throughput/non-proof-of-work
// scenarios:
interval = setInterval(function() {
contract._eth.getTransactionReceipt(contract.transactionHash, function(e, receipt){ contract._eth.getTransactionReceipt(contract.transactionHash, function(e, receipt){
if(receipt && !callbackFired) { if(receipt && !callbackFired) {
@ -2927,13 +2937,9 @@ var checkForContractAddress = function(contract, callback){
if(callbackFired || !code) if(callbackFired || !code)
return; return;
filter.stopWatching(); stop();
callbackFired = true;
if(code.length > 2) { if(code.length > 2) {
// console.log('Contract code deployed!');
contract.address = receipt.contractAddress; contract.address = receipt.contractAddress;
// attach events and methods again after we have // attach events and methods again after we have
@ -2943,7 +2949,6 @@ var checkForContractAddress = function(contract, callback){
// call callback for the second time // call callback for the second time
if(callback) if(callback)
callback(null, contract); callback(null, contract);
} else { } else {
if(callback) if(callback)
callback(new Error('The contract code couldn\'t be stored, please check your gas amount.')); callback(new Error('The contract code couldn\'t be stored, please check your gas amount.'));
@ -2953,9 +2958,7 @@ var checkForContractAddress = function(contract, callback){
}); });
} }
}); });
} }, 250);
}
});
}; };
/** /**