Skip to content

Commit

Permalink
Copy block params for historical logs request (#3455)
Browse files Browse the repository at this point in the history
  • Loading branch information
cgewecke authored Apr 6, 2020
1 parent cae5334 commit 0ffac4b
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 2 deletions.
6 changes: 5 additions & 1 deletion packages/web3-core-subscriptions/src/subscription.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,9 +232,13 @@ Subscription.prototype.subscribe = function() {
// get past logs, if fromBlock is available
if(payload.params[0] === 'logs' && _.isObject(payload.params[1]) && payload.params[1].hasOwnProperty('fromBlock') && isFinite(payload.params[1].fromBlock)) {
// send the subscription request

// copy the params to avoid race-condition with deletion below this block
var blockParams = Object.assign({}, payload.params[1]);

this.options.requestManager.send({
method: 'eth_getLogs',
params: [payload.params[1]]
params: [blockParams]
}, function (err, logs) {
if(!err) {
logs.forEach(function(log){
Expand Down
50 changes: 49 additions & 1 deletion test/e2e.contract.events.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ describe('contract.events [ @E2E ]', function() {
var accounts;
var basic;
var instance;
var port;

var basicOptions = {
data: Basic.bytecode,
Expand All @@ -21,7 +22,7 @@ describe('contract.events [ @E2E ]', function() {
};

beforeEach(async function(){
var port = utils.getWebsocketPort();
port = utils.getWebsocketPort();

web3 = new Web3('ws://localhost:' + port);
accounts = await web3.eth.getAccounts();
Expand Down Expand Up @@ -133,6 +134,53 @@ describe('contract.events [ @E2E ]', function() {
});
});

// Regression test for a race-condition where a fresh web3 instance
// subscribing to past events would have its call parameters deleted while it
// made initial Websocket handshake and return an incorrect response.
it('can immediately listen for events in the past', async function(){
this.timeout(15000);

const first = await instance
.methods
.firesEvent(accounts[0], 1)
.send({from: accounts[0]});

const second = await instance
.methods
.firesEvent(accounts[0], 1)
.send({from: accounts[0]});

// Go forward one block...
await utils.mine(web3, accounts[0]);
const latestBlock = await web3.eth.getBlockNumber();

assert(first.blockNumber < latestBlock);
assert(second.blockNumber < latestBlock);

// Re-instantiate web3 & instance to simulate
// subscribing to past events as first request
web3 = new Web3('ws://localhost:' + port);
const newInstance = new web3.eth.Contract(Basic.abi, instance.options.address);

let counter = 0;
await new Promise(async resolve => {
newInstance
.events
.BasicEvent({
fromBlock: 0
})
.on('data', function(event) {
counter++;
assert(event.blockNumber < latestBlock);

if (counter === 2){
this.removeAllListeners();
resolve();
}
});
});
});

it('hears events when subscribed to "logs" (emitter)', function(){
return new Promise(async function(resolve, reject){

Expand Down

0 comments on commit 0ffac4b

Please sign in to comment.