Skip to content
This repository has been archived by the owner on Jul 9, 2021. It is now read-only.

Commit

Permalink
Merge pull request #1082 from 0xProject/fixGetBlocks
Browse files Browse the repository at this point in the history
Fix block fetch error if block not found
  • Loading branch information
fabioberger authored Sep 24, 2018
2 parents 8bce407 + 7516959 commit fc33eac
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { AbiDecoder, intervalUtils, logUtils } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import {
BlockParamLiteral,
BlockWithoutTransactionData,
ContractAbi,
ContractArtifact,
FilterObject,
Expand Down Expand Up @@ -174,7 +175,7 @@ export abstract class ContractWrapper {
throw new Error(ContractWrappersError.SubscriptionAlreadyPresent);
}
this._blockAndLogStreamerIfExists = new BlockAndLogStreamer(
this._web3Wrapper.getBlockAsync.bind(this._web3Wrapper),
this._getBlockOrNullAsync.bind(this),
this._web3Wrapper.getLogsAsync.bind(this._web3Wrapper),
ContractWrapper._onBlockAndLogStreamerError.bind(this, isVerbose),
);
Expand All @@ -194,6 +195,14 @@ export abstract class ContractWrapper {
this._onLogStateChanged.bind(this, isRemoved),
);
}
// This method only exists in order to comply with the expected interface of Blockstream's constructor
private async _getBlockOrNullAsync(): Promise<BlockWithoutTransactionData | null> {
const blockIfExists = await this._web3Wrapper.getBlockIfExistsAsync.bind(this._web3Wrapper);
if (_.isUndefined(blockIfExists)) {
return null;
}
return blockIfExists;
}
// HACK: This should be a package-scoped method (which doesn't exist in TS)
// We don't want this method available in the public interface for all classes
// who inherit from ContractWrapper, and it is only used by the internal implementation
Expand All @@ -212,11 +221,14 @@ export abstract class ContractWrapper {
delete this._blockAndLogStreamerIfExists;
}
private async _reconcileBlockAsync(): Promise<void> {
const latestBlock = await this._web3Wrapper.getBlockAsync(BlockParamLiteral.Latest);
const latestBlockIfExists = await this._web3Wrapper.getBlockIfExistsAsync(BlockParamLiteral.Latest);
if (_.isUndefined(latestBlockIfExists)) {
return; // noop
}
// We need to coerce to Block type cause Web3.Block includes types for mempool blocks
if (!_.isUndefined(this._blockAndLogStreamerIfExists)) {
// If we clear the interval while fetching the block - this._blockAndLogStreamer will be undefined
await this._blockAndLogStreamerIfExists.reconcileNewBlock((latestBlock as any) as Block);
await this._blockAndLogStreamerIfExists.reconcileNewBlock((latestBlockIfExists as any) as Block);
}
}
}
2 changes: 1 addition & 1 deletion packages/contract-wrappers/test/subscription_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ describe('SubscriptionTest', () => {
callback,
);
stubs = [
Sinon.stub((contractWrappers as any)._web3Wrapper, 'getBlockAsync').throws(
Sinon.stub((contractWrappers as any)._web3Wrapper, 'getBlockIfExistsAsync').throws(
new Error('JSON RPC error'),
),
];
Expand Down
5 changes: 4 additions & 1 deletion packages/contracts/test/multisig/multi_sig_with_time_lock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,10 @@ describe('MultiSigWalletWithTimeLock', () => {
expect(confirmRes.logs).to.have.length(2);

const blockNum = await web3Wrapper.getBlockNumberAsync();
const blockInfo = await web3Wrapper.getBlockAsync(blockNum);
const blockInfo = await web3Wrapper.getBlockIfExistsAsync(blockNum);
if (_.isUndefined(blockInfo)) {
throw new Error(`Unexpectedly failed to fetch block at #${blockNum}`);
}
const timestamp = new BigNumber(blockInfo.timestamp);
const confirmationTimeBigNum = new BigNumber(await multiSig.confirmationTimes.callAsync(txId));

Expand Down
7 changes: 5 additions & 2 deletions packages/contracts/test/utils/block_timestamp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ export async function increaseTimeAndMineBlockAsync(seconds: number): Promise<nu
* @returns a new Promise which will resolve with the timestamp in seconds.
*/
export async function getLatestBlockTimestampAsync(): Promise<number> {
const currentBlock = await web3Wrapper.getBlockAsync('latest');
return currentBlock.timestamp;
const currentBlockIfExists = await web3Wrapper.getBlockIfExistsAsync('latest');
if (_.isUndefined(currentBlockIfExists)) {
throw new Error(`Unable to fetch latest block.`);
}
return currentBlockIfExists.timestamp;
}
19 changes: 15 additions & 4 deletions packages/order-watcher/src/order_watcher/event_watcher.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { intervalUtils, logUtils } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { BlockParamLiteral, LogEntry, Provider } from 'ethereum-types';
import { BlockParamLiteral, BlockWithoutTransactionData, LogEntry, Provider } from 'ethereum-types';
import { Block, BlockAndLogStreamer, Log } from 'ethereumjs-blockstream';
import * as _ from 'lodash';

Expand Down Expand Up @@ -62,7 +62,7 @@ export class EventWatcher {
throw new Error(OrderWatcherError.SubscriptionAlreadyPresent);
}
this._blockAndLogStreamerIfExists = new BlockAndLogStreamer(
this._web3Wrapper.getBlockAsync.bind(this._web3Wrapper),
this._getBlockOrNullAsync.bind(this),
this._web3Wrapper.getLogsAsync.bind(this._web3Wrapper),
this._onBlockAndLogStreamerError.bind(this),
);
Expand All @@ -82,6 +82,14 @@ export class EventWatcher {
this._onLogStateChangedAsync.bind(this, callback, isRemoved),
);
}
// This method only exists in order to comply with the expected interface of Blockstream's constructor
private async _getBlockOrNullAsync(): Promise<BlockWithoutTransactionData | null> {
const blockIfExists = await this._web3Wrapper.getBlockIfExistsAsync.bind(this._web3Wrapper);
if (_.isUndefined(blockIfExists)) {
return null;
}
return blockIfExists;
}
private _stopBlockAndLogStream(): void {
if (_.isUndefined(this._blockAndLogStreamerIfExists)) {
throw new Error(OrderWatcherError.SubscriptionNotFound);
Expand All @@ -100,11 +108,14 @@ export class EventWatcher {
await this._emitDifferencesAsync(log, isRemoved ? LogEventState.Removed : LogEventState.Added, callback);
}
private async _reconcileBlockAsync(): Promise<void> {
const latestBlock = await this._web3Wrapper.getBlockAsync(this._stateLayer);
const latestBlockIfExists = await this._web3Wrapper.getBlockIfExistsAsync(this._stateLayer);
if (_.isUndefined(latestBlockIfExists)) {
return; // noop
}
// We need to coerce to Block type cause Web3.Block includes types for mempool blocks
if (!_.isUndefined(this._blockAndLogStreamerIfExists)) {
// If we clear the interval while fetching the block - this._blockAndLogStreamer will be undefined
await this._blockAndLogStreamerIfExists.reconcileNewBlock((latestBlock as any) as Block);
await this._blockAndLogStreamerIfExists.reconcileNewBlock((latestBlockIfExists as any) as Block);
}
}
private async _emitDifferencesAsync(
Expand Down
10 changes: 10 additions & 0 deletions packages/web3-wrapper/CHANGELOG.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
[
{
"version": "3.0.0",
"changes": [
{
"note":
"Rename `getBlockAsync` to `getBlockIfExistsAsync` and rather then throw if the requested block wasn't found, return undefined.",
"pr": 1082
}
]
},
{
"version": "2.0.3",
"changes": [
Expand Down
27 changes: 18 additions & 9 deletions packages/web3-wrapper/src/web3_wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,23 +329,29 @@ export class Web3Wrapper {
/**
* Fetch a specific Ethereum block without transaction data
* @param blockParam The block you wish to fetch (blockHash, blockNumber or blockLiteral)
* @returns The requested block without transaction data
* @returns The requested block without transaction data, or undefined if block was not found
* (e.g the node isn't fully synced, there was a block re-org and the requested block was uncles, etc...)
*/
public async getBlockAsync(blockParam: string | BlockParam): Promise<BlockWithoutTransactionData> {
public async getBlockIfExistsAsync(
blockParam: string | BlockParam,
): Promise<BlockWithoutTransactionData | undefined> {
Web3Wrapper._assertBlockParamOrString(blockParam);
const encodedBlockParam = marshaller.marshalBlockParam(blockParam);
const method = utils.isHexStrict(blockParam) ? 'eth_getBlockByHash' : 'eth_getBlockByNumber';
const shouldIncludeTransactionData = false;
const blockWithoutTransactionDataWithHexValues = await this._sendRawPayloadAsync<
const blockWithoutTransactionDataWithHexValuesOrNull = await this._sendRawPayloadAsync<
BlockWithoutTransactionDataRPC
>({
method,
params: [encodedBlockParam, shouldIncludeTransactionData],
});
const blockWithoutTransactionData = marshaller.unmarshalIntoBlockWithoutTransactionData(
blockWithoutTransactionDataWithHexValues,
);
return blockWithoutTransactionData;
let blockWithoutTransactionDataIfExists;
if (!_.isNull(blockWithoutTransactionDataWithHexValuesOrNull)) {
blockWithoutTransactionDataIfExists = marshaller.unmarshalIntoBlockWithoutTransactionData(
blockWithoutTransactionDataWithHexValuesOrNull,
);
}
return blockWithoutTransactionDataIfExists;
}
/**
* Fetch a specific Ethereum block with transaction data
Expand Down Expand Up @@ -376,8 +382,11 @@ export class Web3Wrapper {
*/
public async getBlockTimestampAsync(blockParam: string | BlockParam): Promise<number> {
Web3Wrapper._assertBlockParamOrString(blockParam);
const { timestamp } = await this.getBlockAsync(blockParam);
return timestamp;
const blockIfExists = await this.getBlockIfExistsAsync(blockParam);
if (_.isUndefined(blockIfExists)) {
throw new Error(`Failed to fetch block with blockParam: ${JSON.stringify(blockParam)}`);
}
return blockIfExists.timestamp;
}
/**
* Retrieve the user addresses available through the backing provider
Expand Down
34 changes: 23 additions & 11 deletions packages/web3-wrapper/test/web3_wrapper_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,28 +85,40 @@ describe('Web3Wrapper tests', () => {
expect(typeof blockNumber).to.be.equal('number');
});
});
describe('#getBlockAsync', () => {
describe('#getBlockIfExistsAsync', () => {
it('gets block when supplied a valid BlockParamLiteral value', async () => {
const blockParamLiteral = BlockParamLiteral.Earliest;
const block = await web3Wrapper.getBlockAsync(blockParamLiteral);
expect(block.number).to.be.equal(0);
expect(utils.isBigNumber(block.difficulty)).to.equal(true);
expect(_.isNumber(block.gasLimit)).to.equal(true);
const blockIfExists = await web3Wrapper.getBlockIfExistsAsync(blockParamLiteral);
if (_.isUndefined(blockIfExists)) {
throw new Error('Expected block to exist');
}
expect(blockIfExists.number).to.be.equal(0);
expect(utils.isBigNumber(blockIfExists.difficulty)).to.equal(true);
expect(_.isNumber(blockIfExists.gasLimit)).to.equal(true);
});
it('gets block when supplied a block number', async () => {
const blockParamLiteral = 0;
const block = await web3Wrapper.getBlockAsync(blockParamLiteral);
expect(block.number).to.be.equal(0);
const blockIfExists = await web3Wrapper.getBlockIfExistsAsync(blockParamLiteral);
if (_.isUndefined(blockIfExists)) {
throw new Error('Expected block to exist');
}
expect(blockIfExists.number).to.be.equal(0);
});
it('gets block when supplied a block hash', async () => {
const blockParamLiteral = 0;
const block = await web3Wrapper.getBlockAsync(blockParamLiteral);
const sameBlock = await web3Wrapper.getBlockAsync(block.hash as string);
expect(sameBlock.number).to.be.equal(0);
const blockIfExists = await web3Wrapper.getBlockIfExistsAsync(blockParamLiteral);
if (_.isUndefined(blockIfExists)) {
throw new Error('Expected block to exist');
}
const sameBlockIfExists = await web3Wrapper.getBlockIfExistsAsync(blockIfExists.hash as string);
if (_.isUndefined(sameBlockIfExists)) {
throw new Error('Expected block to exist');
}
expect(sameBlockIfExists.number).to.be.equal(0);
});
it('should throw if supplied invalid blockParam value', async () => {
const invalidBlockParam = 'deadbeef';
expect(web3Wrapper.getBlockAsync(invalidBlockParam)).to.eventually.to.be.rejected();
expect(web3Wrapper.getBlockIfExistsAsync(invalidBlockParam)).to.eventually.to.be.rejected();
});
});
describe('#getBlockWithTransactionDataAsync', () => {
Expand Down

0 comments on commit fc33eac

Please sign in to comment.