From 6f50dc5058012225acc899f3859c4478562761a9 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Tue, 12 May 2020 15:22:32 -0700 Subject: [PATCH 1/2] Add tests to check for proper subscription behavior --- test/method.buildCall.js | 191 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 184 insertions(+), 7 deletions(-) diff --git a/test/method.buildCall.js b/test/method.buildCall.js index 49f4e1f0b49..f3b769117eb 100644 --- a/test/method.buildCall.js +++ b/test/method.buildCall.js @@ -1,7 +1,8 @@ var chai = require('chai'); var assert = chai.assert; var formatters = require('../packages/web3-core-helpers/src/formatters.js'); -var FakeHttpProvider = require('./helpers/FakeIpcProvider'); +var FakeHttpProvider = require('./helpers/FakeHttpProvider'); +var FakeIpcProvider = require('./helpers/FakeIpcProvider'); var Eth = require('../packages/web3-eth'); var Method = require('../packages/web3-core-method'); @@ -220,7 +221,7 @@ describe('lib/web3/method', function () { }); var succeedOnReceipt = function () { - var provider = new FakeHttpProvider(); + var provider = new FakeIpcProvider(); var eth = new Eth(provider); var method = new Method({ name: 'sendTransaction', @@ -343,7 +344,7 @@ describe('lib/web3/method', function () { var succeedwhenDeploying = function () { - var provider = new FakeHttpProvider(); + var provider = new FakeIpcProvider(); var eth = new Eth(provider); var method = new Method({ name: 'sendTransaction', @@ -461,7 +462,7 @@ describe('lib/web3/method', function () { }); var failOnCodeEmpty = function () { - var provider = new FakeHttpProvider(); + var provider = new FakeIpcProvider(); var eth = new Eth(provider); var method = new Method({ name: 'sendTransaction', @@ -561,7 +562,7 @@ describe('lib/web3/method', function () { }); var failOnMissingAddress = function () { - var provider = new FakeHttpProvider(); + var provider = new FakeIpcProvider(); var eth = new Eth(provider); var method = new Method({ name: 'sendTransaction', @@ -661,7 +662,7 @@ describe('lib/web3/method', function () { }); var failOnTimeout = function () { - var provider = new FakeHttpProvider(); + var provider = new FakeIpcProvider(); var eth = new Eth(provider); var method = new Method({ name: 'sendTransaction', @@ -750,7 +751,7 @@ describe('lib/web3/method', function () { }); it('should give confirmation receipts with on("confirmation", ...) when subscribing "sendTransaction"', function (done) { - var provider = new FakeHttpProvider(); + var provider = new FakeIpcProvider(); var eth = new Eth(provider); var method = new Method({ name: 'sendTransaction', @@ -860,6 +861,182 @@ describe('lib/web3/method', function () { }); }); + + it('should subscribe to new blocks if using IpcProvider', function (done) { + const provider = new FakeIpcProvider(); + const eth = new Eth(provider); + + const method = new Method({ + name: 'sendTransaction', + call: 'eth_sendTransaction', + params: 1, + inputFormatter: [formatters.inputTransactionFormatter] + }); + method.setRequestManager(eth._requestManager, eth); + + // generate send function + const send = method.buildCall(); + + // add results + provider.injectValidation(function (payload) { + assert.equal(payload.method, 'eth_sendTransaction'); + assert.deepEqual(payload.params, [{ + from: '0x11f4d0a3c12e86b4b5f39b213f7e19d048276dae', + to: '0x11f4d0a3c12e86b4b5f39b213f7e19d048276dae', + value: '0xa', + gasPrice: "0x574d94bba" + }]); + }); + provider.injectResult('0x1234567453543456321456321'); // tx hash + + provider.injectValidation(function (payload) { + assert.equal(payload.method, 'eth_getTransactionReceipt'); + assert.deepEqual(payload.params, ['0x1234567453543456321456321']); + }); + provider.injectResult(null); + + provider.injectValidation(function (payload) { + // here is the check. + // will be `eth_subscribe` if subscribing. + // will be `eth_getTransactionReceipt` if polling. + assert.equal(payload.method, 'eth_subscribe'); + assert.deepEqual(payload.params, ['newHeads']); + done(); + }); + + send({ + from: '0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe', + to: '0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe', + value: '0xa', + gasPrice: '23435234234' + }) + }); + + it('should use polling if using HttpProvider', function (done) { + const provider = new FakeHttpProvider(); + const eth = new Eth(provider); + + const method = new Method({ + name: 'sendTransaction', + call: 'eth_sendTransaction', + params: 1, + inputFormatter: [formatters.inputTransactionFormatter] + }); + method.setRequestManager(eth._requestManager, eth); + + // generate send function + const send = method.buildCall(); + + // add results + provider.injectValidation(function (payload) { + assert.equal(payload.method, 'eth_sendTransaction'); + assert.deepEqual(payload.params, [{ + from: '0x11f4d0a3c12e86b4b5f39b213f7e19d048276dae', + to: '0x11f4d0a3c12e86b4b5f39b213f7e19d048276dae', + value: '0xa', + gasPrice: "0x574d94bba" + }]); + }); + provider.injectResult('0x1234567453543456321456321'); // tx hash + + provider.injectValidation(function (payload) { + assert.equal(payload.method, 'eth_getTransactionReceipt'); + assert.deepEqual(payload.params, ['0x1234567453543456321456321']); + }); + provider.injectResult(null); + + provider.injectValidation(function (payload) { + // here is the check. + // will be `eth_subscribe` if subscribing. + // will be `eth_getTransactionReceipt` if polling. + assert.equal(payload.method, 'eth_getTransactionReceipt'); + assert.deepEqual(payload.params, ['0x1234567453543456321456321']); + done(); + }); + + send({ + from: '0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe', + to: '0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe', + value: '0xa', + gasPrice: '23435234234' + }) + }); + + it('should use polling if using provider with method `on` but no subscription capabilities', function (done) { + this.timeout(5000); + + const provider = new FakeHttpProvider(); + // provider with method 'on' but no subscription capabilities should use polling + provider.on = (...args) => {} + const eth = new Eth(provider); + + const method = new Method({ + name: 'sendTransaction', + call: 'eth_sendTransaction', + params: 1, + inputFormatter: [formatters.inputTransactionFormatter] + }); + method.setRequestManager(eth._requestManager, eth); + + // generate send function + const send = method.buildCall(); + + // add results + provider.injectValidation(function (payload) { + assert.equal(payload.method, 'eth_sendTransaction'); + assert.deepEqual(payload.params, [{ + from: '0x11f4d0a3c12e86b4b5f39b213f7e19d048276dae', + to: '0x11f4d0a3c12e86b4b5f39b213f7e19d048276dae', + value: '0xa', + gasPrice: "0x574d94bba" + }]); + }); + provider.injectResult('0x1234567453543456321456321'); // tx hash + + provider.injectValidation(function (payload) { + assert.equal(payload.method, 'eth_getTransactionReceipt'); + assert.deepEqual(payload.params, ['0x1234567453543456321456321']); + }); + provider.injectResult(null); + + provider.injectValidation(function (payload) { + // here is the check. + // first will try subscribing with `eth_subscribe`. + assert.equal(payload.method, 'eth_subscribe'); + assert.deepEqual(payload.params, ['newHeads']); + }); + provider.injectResult(null); + + // after failing with `eth_subscribe`, + // it should start polling with `eth_getTransactionReceipt` + provider.injectValidation(function (payload) { + assert.equal(payload.method, 'eth_getTransactionReceipt'); + assert.deepEqual(payload.params, ['0x1234567453543456321456321']); + }); + provider.injectResult(null); + + // second poll + provider.injectValidation(function (payload) { + assert.equal(payload.method, 'eth_getTransactionReceipt'); + assert.deepEqual(payload.params, ['0x1234567453543456321456321']); + }); + provider.injectResult(null); + + // third poll + provider.injectValidation(function (payload) { + assert.equal(payload.method, 'eth_getTransactionReceipt'); + assert.deepEqual(payload.params, ['0x1234567453543456321456321']); + done(); + }); + provider.injectResult(null); + + send({ + from: '0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe', + to: '0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe', + value: '0xa', + gasPrice: '23435234234' + }) + }); }); }); From 911aab14b7ec745d04a08cb84a30d078e22f5b83 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Tue, 12 May 2020 15:39:40 -0700 Subject: [PATCH 2/2] Improve check if a provider supports subscriptions --- packages/web3-core-method/src/index.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/web3-core-method/src/index.js b/packages/web3-core-method/src/index.js index 3d4a06ed1fd..af83bfcf3b9 100644 --- a/packages/web3-core-method/src/index.js +++ b/packages/web3-core-method/src/index.js @@ -539,12 +539,22 @@ Method.prototype._confirmTransaction = function (defer, result, payload) { // start watching for confirmation depending on the support features of the provider var startWatching = function (existingReceipt) { - // if provider allows PUB/SUB - if (_.isFunction(this.requestManager.provider.on)) { - _ethereumCall.subscribe('newBlockHeaders', checkConfirmation.bind(null, existingReceipt, false)); - } else { + const startInterval = () => { intervalId = setInterval(checkConfirmation.bind(null, existingReceipt, true), 1000); } + + if (!this.requestManager.provider.on) { + startInterval() + } else { + _ethereumCall.subscribe('newBlockHeaders', function (err, blockHeader, sub) { + if (err || !blockHeader) { + // fall back to polling + startInterval() + } else { + checkConfirmation(existingReceipt, false, err, blockHeader, sub); + } + }) + } }.bind(this);