@nomiclabs/buidler
Version:
Buidler is an extensible developer tool that helps smart contract developers increase productivity by reliably bringing together the tools they want.
143 lines (120 loc) • 2.85 kB
text/typescript
import Bloom from "@nomiclabs/ethereumjs-vm/dist/bloom";
import { BN, bufferToHex, toBuffer } from "ethereumjs-util";
import { RpcLogOutput } from "./output";
export const LATEST_BLOCK = new BN(-1);
export enum Type {
LOGS_SUBSCRIPTION = 0,
PENDING_TRANSACTION_SUBSCRIPTION = 1,
BLOCK_SUBSCRIPTION = 2,
}
export interface FilterCriteria {
fromBlock: BN;
toBlock: BN;
addresses: Buffer[];
normalizedTopics: Array<Array<Buffer | null> | null>;
}
export interface Filter {
id: BN;
type: Type;
criteria?: FilterCriteria;
deadline: Date;
hashes: string[];
logs: RpcLogOutput[];
subscription: boolean;
}
export function bloomFilter(
bloom: Bloom,
addresses: Buffer[],
normalizedTopics: Array<Array<Buffer | null> | null>
): boolean {
if (addresses.length > 0) {
let included = false;
for (const address of addresses) {
if (bloom.check(address)) {
included = true;
break;
}
}
if (!included) {
return false;
}
}
for (const sub of normalizedTopics) {
if (sub == null || sub.length === 0) {
continue;
}
let included = false;
for (const topic of sub) {
if (topic != null && bloom.check(topic)) {
included = true;
break;
}
}
if (!included) {
return false;
}
}
return true;
}
export function filterLogs(
logs: RpcLogOutput[],
criteria: FilterCriteria
): RpcLogOutput[] {
const filteredLogs: RpcLogOutput[] = [];
for (const log of logs) {
const blockNumber = new BN(toBuffer(log.blockNumber!));
if (blockNumber.lt(criteria.fromBlock)) {
continue;
}
if (
!criteria.toBlock.eq(LATEST_BLOCK) &&
blockNumber.gt(criteria.toBlock)
) {
continue;
}
if (
criteria.addresses.length !== 0 &&
!includes(criteria.addresses, toBuffer(log.address))
) {
continue;
}
if (!topicMatched(criteria.normalizedTopics, log.topics)) {
continue;
}
filteredLogs.push(log);
}
return filteredLogs;
}
export function includes(addresses: Buffer[], a: Buffer): boolean {
for (const address of addresses) {
if (Buffer.compare(address, a) === 0) {
return true;
}
}
return false;
}
export function topicMatched(
normalizedTopics: Array<Array<Buffer | null> | null>,
logTopics: string[]
): boolean {
for (let i = 0; i < normalizedTopics.length; i++) {
if (normalizedTopics.length > logTopics.length) {
return false;
}
const sub = normalizedTopics[i];
if (sub == null || sub.length === 0) {
continue;
}
let match: boolean = false;
for (const topic of sub) {
if (topic === null || logTopics[i] === bufferToHex(topic)) {
match = true;
break;
}
}
if (!match) {
return false;
}
}
return true;
}