Skip to content

Commit 2576905

Browse files
authored
Merge pull request #25 from ethereumjs/micah
Switches to using new eth_getLogs by blockHash RPC.
2 parents e3f0041 + b0d6d9e commit 2576905

File tree

9 files changed

+106
-124
lines changed

9 files changed

+106
-124
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
A library to turn an unreliable remote source of Ethereum blocks into a reliable stream of blocks. Handles block and log removals on chain reorganization and block and log backfills on skipped blocks.
44

5+
# Requirements for supported Ethereum node
6+
Blockstream requires support for [EIP-234](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-234.md) in the configured Ethereum node. EIP-234 was merged Jul 28, 2018 and implemented in Geth and Parity shortly after. Versions that provide the needed functionality:
7+
- Parity: v2.1.0+
8+
- geth: v1.8.13+
9+
510
# Usage
611

712
## Full Example

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ethereumjs-blockstream",
3-
"version": "5.0.0",
3+
"version": "6.0.1",
44
"description": "A library to turn an unreliable remote source of Ethereum blocks into a reliable stream of blocks with removals on re-orgs and backfills on skips.",
55
"main": "output/source/index.js",
66
"types": "output/source/index.d.ts",

source/block-reconciler.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Block } from "./models/block";
22
import { BlockHistory } from "./models/block-history";
3+
import { parseHexInt } from "./utilities";
34
import { List as ImmutableList } from "immutable";
45

56
type GetBlockByHash<TBlock> = (hash: string) => Promise<TBlock|null>;
@@ -50,7 +51,7 @@ const backfill = async <TBlock extends Block>(getBlockByHash: GetBlockByHash<TBl
5051
return await rollback(blockHistory, onBlockRemoved);
5152
const parentBlock = await getBlockByHash(newBlock.parentHash);
5253
if (parentBlock === null) throw new Error("Failed to fetch parent block.");
53-
if (parseInt(parentBlock.number, 16) + blockRetention < parseInt(blockHistory.last().number, 16))
54+
if (parseHexInt(parentBlock.number) + blockRetention < parseHexInt(blockHistory.last().number))
5455
return await rollback(blockHistory, onBlockRemoved);
5556
blockHistory = await reconcileBlockHistory(getBlockByHash, blockHistory, parentBlock, onBlockAdded, onBlockRemoved, blockRetention);
5657
return await reconcileBlockHistory(getBlockByHash, blockHistory, newBlock, onBlockAdded, onBlockRemoved, blockRetention);
@@ -77,7 +78,7 @@ const isFirstBlock = <TBlock extends Block>(blockHistory: BlockHistory<TBlock>):
7778
}
7879

7980
const isOlderThanOldestBlock = <TBlock extends Block>(blockHistory: BlockHistory<TBlock>, newBlock: TBlock): boolean => {
80-
return parseInt(blockHistory.first().number, 16) > parseInt(newBlock.number, 16);
81+
return parseHexInt(blockHistory.first().number) > parseHexInt(newBlock.number);
8182
}
8283

8384
const isAlreadyInHistory = <TBlock extends Block>(blockHistory: BlockHistory<TBlock>, newBlock: TBlock): boolean => {

source/log-reconciler.ts

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Block } from "./models/block";
22
import { Log } from "./models/log";
33
import { Filter, FilterOptions } from "./models/filters";
44
import { LogHistory } from "./models/log-history";
5-
import { List as ImmutableList } from "immutable";
5+
import { parseHexInt } from "./utilities";
66

77
export const reconcileLogHistoryWithAddedBlock = async <TBlock extends Block, TLog extends Log>(
88
getLogs: (filterOptions: FilterOptions) => Promise<TLog[]>,
@@ -14,22 +14,21 @@ export const reconcileLogHistoryWithAddedBlock = async <TBlock extends Block, TL
1414
): Promise<LogHistory<TLog>> => {
1515
logHistory = await logHistory;
1616
const logs = await getFilteredLogs(getLogs, newBlock, filters);
17-
ensureBlockhash(newBlock, logs);
1817
logHistory = await addNewLogsToHead(logHistory, logs, onLogAdded);
1918
logHistory = await pruneOldLogs(logHistory, newBlock, historyBlockLength);
2019
return logHistory;
2120
}
2221

2322
const getFilteredLogs = async <TBlock extends Block, TLog extends Log>(getLogs: (filterOptions: FilterOptions) => Promise<Array<TLog>>, newBlock: TBlock, filters: Array<Filter>): Promise<Array<TLog>> => {
2423
const logPromises = filters
25-
.map(filter => ({ fromBlock: newBlock.number, toBlock: newBlock.number, address: filter.address, topics: filter.topics, }))
24+
.map(filter => ({ blockHash: newBlock.hash, address: filter.address, topics: filter.topics, }))
2625
.map(filter => getLogs(filter));
2726
const nestedLogs = await Promise.all(logPromises);
2827
return nestedLogs.reduce((allLogs, logs) => allLogs.concat(logs), []);
2928
}
3029

3130
const addNewLogsToHead = async <TLog extends Log>(logHistory: LogHistory<TLog>, newLogs: Array<TLog>, onLogAdded: (log: TLog) => Promise<void>): Promise<LogHistory<TLog>> => {
32-
const sortedLogs = newLogs.sort((logA, logB) => parseInt(logA.logIndex, 16) - parseInt(logB.logIndex, 16));
31+
const sortedLogs = newLogs.sort((logA, logB) => parseHexInt(logA.logIndex) - parseHexInt(logB.logIndex));
3332
for (const logToAdd of sortedLogs) {
3433
// we may already have this log because two filters can return the same log
3534
if (logHistory.some(logInHistory => logInHistory!.blockHash === logToAdd.blockHash && logInHistory!.logIndex === logToAdd.logIndex)) continue;
@@ -41,7 +40,7 @@ const addNewLogsToHead = async <TLog extends Log>(logHistory: LogHistory<TLog>,
4140

4241
const pruneOldLogs = async <TBlock extends Block, TLog extends Log>(logHistory: LogHistory<TLog>, newBlock: TBlock, historyBlockLength: number): Promise<LogHistory<TLog>> => {
4342
// `log!` is required until the next major version of `immutable` is published to NPM (current version 3.8.2) which improves the type definitions
44-
return logHistory.skipUntil(log => parseInt(newBlock.number, 16) - parseInt(log!.blockNumber, 16) < historyBlockLength).toList();
43+
return logHistory.skipUntil(log => parseHexInt(newBlock.number) - parseHexInt(log!.blockNumber) < historyBlockLength).toList();
4544
}
4645

4746
const addNewLogToHead = async <TLog extends Log>(logHistory: LogHistory<TLog>, newLog: TLog, onLogAdded: (log: TLog) => Promise<void>): Promise<LogHistory<TLog>> => {
@@ -53,22 +52,15 @@ const addNewLogToHead = async <TLog extends Log>(logHistory: LogHistory<TLog>, n
5352

5453
const ensureOrder = <TLog extends Log>(headLog: TLog | undefined, newLog: TLog) => {
5554
if (headLog === undefined) return;
56-
const headBlockNumber = parseInt(headLog.blockNumber, 16);
57-
const newLogBlockNumber = parseInt(newLog.blockNumber, 16);
55+
const headBlockNumber = parseHexInt(headLog.blockNumber);
56+
const newLogBlockNumber = parseHexInt(newLog.blockNumber);
5857
if (headBlockNumber > newLogBlockNumber) throw new Error(`received log for a block (${newLogBlockNumber}) older than current head log's block (${headBlockNumber})`);
5958
if (headBlockNumber !== newLogBlockNumber) return;
60-
const headLogIndex = parseInt(headLog.logIndex, 16);
61-
const newLogIndex = parseInt(newLog.logIndex, 16);
59+
const headLogIndex = parseHexInt(headLog.logIndex);
60+
const newLogIndex = parseHexInt(newLog.logIndex);
6261
if (headLogIndex >= newLogIndex) throw new Error(`received log with same block number (${newLogBlockNumber}) but index (${newLogIndex}) is the same or older than previous index (${headLogIndex})`);
6362
}
6463

65-
const ensureBlockhash = <TBlock extends Block, TLog extends Log>(block: TBlock, logs: Array<TLog>) => {
66-
// FIXME: This technique for verifying we got the right logs will not work if there were no logs present in the block! This means it is possible to miss logs. Can be fixed once https://eips.ethereum.org/EIPS/eip-234 is implemented
67-
logs.forEach(log => {
68-
if (log.blockHash !== block.hash) throw new Error(`Received log for block hash ${log.blockHash} when asking for logs of block ${block.hash}.`);
69-
});
70-
}
71-
7264
export const reconcileLogHistoryWithRemovedBlock = async <TBlock extends Block, TLog extends Log>(
7365
logHistory: LogHistory<TLog>|Promise<LogHistory<TLog>>,
7466
removedBlock: TBlock,

source/models/filters.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,5 @@ export interface Filter {
44
}
55

66
export interface FilterOptions extends Filter {
7-
readonly fromBlock?: string;
8-
readonly toBlock?: string;
7+
readonly blockHash: string
98
}

source/utilities.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export const parseHexInt = (value: string) => {
2+
const result = Number.parseInt(value, 16);
3+
if (!Number.isFinite(result)) throw new Error(`${value} is not a hex encoded integer, parsing returned ${result}.`);
4+
return result;
5+
}

tests/helpers.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ export function getLogsFactory(logsPerFilter: number, fork: string = "AAAA") {
2424
const logs = [];
2525
let logIndex = 0;
2626
for (let i = 0; i < logsPerFilter; ++i) {
27-
const blockNumber = parseInt(filterOptions.toBlock!, 16);
28-
logs.push(new MockLog(blockNumber, logIndex++, fork));
27+
const blockHash = filterOptions.blockHash;
28+
logs.push(new MockLog(blockHash, logIndex++, fork));
2929
}
3030
return logs;
3131
};
@@ -71,10 +71,10 @@ export class MockLog implements Log {
7171
readonly data: string = "0x0000000000000000000000000000000000000000000000000000000000000000";
7272
readonly topics: string[] = [];
7373

74-
constructor(blockNumber: number, logIndex: number = 0x0, fork: string = "AAAA") {
75-
const blockNumberAsHex = blockNumber.toString(16);
76-
this.blockNumber = "0x" + blockNumberAsHex;
77-
this.blockHash = `0xbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0c${fork}${("0000" + blockNumberAsHex).substring(blockNumberAsHex.length)}`;
74+
constructor(blockHash: string, logIndex: number = 0x0, fork: string = "AAAA") {
75+
const blockNumber = parseInt(blockHash.substring(62), 16);
76+
this.blockNumber = `0x${blockNumber.toString(16)}`;
77+
this.blockHash = blockHash;
7878
this.logIndex = `0x${logIndex.toString(16)}`;
7979
this.transactionIndex = this.logIndex;
8080
}

0 commit comments

Comments
 (0)