UNPKG

5.81 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.createBlockCacheMiddleware = void 0;
4const json_rpc_engine_1 = require("json-rpc-engine");
5const cache_1 = require("./utils/cache");
6// `<nil>` comes from https://github.com/ethereum/go-ethereum/issues/16925
7const emptyValues = [undefined, null, '\u003cnil\u003e'];
8//
9// Cache Strategies
10//
11class BlockCacheStrategy {
12 constructor() {
13 this.cache = {};
14 }
15 getBlockCacheForPayload(_payload, blockNumberHex) {
16 const blockNumber = Number.parseInt(blockNumberHex, 16);
17 let blockCache = this.cache[blockNumber];
18 // create new cache if necesary
19 if (!blockCache) {
20 const newCache = {};
21 this.cache[blockNumber] = newCache;
22 blockCache = newCache;
23 }
24 return blockCache;
25 }
26 async get(payload, requestedBlockNumber) {
27 // lookup block cache
28 const blockCache = this.getBlockCacheForPayload(payload, requestedBlockNumber);
29 // lookup payload in block cache
30 const identifier = cache_1.cacheIdentifierForPayload(payload, true);
31 return identifier ? blockCache[identifier] : undefined;
32 }
33 async set(payload, requestedBlockNumber, result) {
34 // check if we can cached this result
35 const canCacheResult = this.canCacheResult(payload, result);
36 if (!canCacheResult) {
37 return;
38 }
39 // set the value in the cache
40 const identifier = cache_1.cacheIdentifierForPayload(payload, true);
41 if (!identifier) {
42 return;
43 }
44 const blockCache = this.getBlockCacheForPayload(payload, requestedBlockNumber);
45 blockCache[identifier] = result;
46 }
47 canCacheRequest(payload) {
48 // check request method
49 if (!cache_1.canCache(payload)) {
50 return false;
51 }
52 // check blockTag
53 const blockTag = cache_1.blockTagForPayload(payload);
54 if (blockTag === 'pending') {
55 return false;
56 }
57 // can be cached
58 return true;
59 }
60 canCacheResult(payload, result) {
61 // never cache empty values (e.g. undefined)
62 if (emptyValues.includes(result)) {
63 return false;
64 }
65 // check if transactions have block reference before caching
66 if (payload.method &&
67 ['eth_getTransactionByHash', 'eth_getTransactionReceipt'].includes(payload.method)) {
68 if (!result ||
69 !result.blockHash ||
70 result.blockHash ===
71 '0x0000000000000000000000000000000000000000000000000000000000000000') {
72 return false;
73 }
74 }
75 // otherwise true
76 return true;
77 }
78 // removes all block caches with block number lower than `oldBlockHex`
79 clearBefore(oldBlockHex) {
80 const oldBlockNumber = Number.parseInt(oldBlockHex, 16);
81 // clear old caches
82 Object.keys(this.cache)
83 .map(Number)
84 .filter((num) => num < oldBlockNumber)
85 .forEach((num) => delete this.cache[num]);
86 }
87}
88function createBlockCacheMiddleware({ blockTracker, } = {}) {
89 // validate options
90 if (!blockTracker) {
91 throw new Error('createBlockCacheMiddleware - No PollingBlockTracker specified');
92 }
93 // create caching strategies
94 const blockCache = new BlockCacheStrategy();
95 const strategies = {
96 perma: blockCache,
97 block: blockCache,
98 fork: blockCache,
99 };
100 return json_rpc_engine_1.createAsyncMiddleware(async (req, res, next) => {
101 // allow cach to be skipped if so specified
102 if (req.skipCache) {
103 return next();
104 }
105 // check type and matching strategy
106 const type = cache_1.cacheTypeForPayload(req);
107 const strategy = strategies[type];
108 // If there's no strategy in place, pass it down the chain.
109 if (!strategy) {
110 return next();
111 }
112 // If the strategy can't cache this request, ignore it.
113 if (!strategy.canCacheRequest(req)) {
114 return next();
115 }
116 // get block reference (number or keyword)
117 let blockTag = cache_1.blockTagForPayload(req);
118 if (!blockTag) {
119 blockTag = 'latest';
120 }
121 // get exact block number
122 let requestedBlockNumber;
123 if (blockTag === 'earliest') {
124 // this just exists for symmetry with "latest"
125 requestedBlockNumber = '0x00';
126 }
127 else if (blockTag === 'latest') {
128 // fetch latest block number
129 const latestBlockNumber = await blockTracker.getLatestBlock();
130 // clear all cache before latest block
131 blockCache.clearBefore(latestBlockNumber);
132 requestedBlockNumber = latestBlockNumber;
133 }
134 else {
135 // We have a hex number
136 requestedBlockNumber = blockTag;
137 }
138 // end on a hit, continue on a miss
139 const cacheResult = await strategy.get(req, requestedBlockNumber);
140 if (cacheResult === undefined) {
141 // cache miss
142 // wait for other middleware to handle request
143 // eslint-disable-next-line node/callback-return
144 await next();
145 // add result to cache
146 // it's safe to cast res.result as Block, due to runtime type checks
147 // performed when strategy.set is called
148 await strategy.set(req, requestedBlockNumber, res.result);
149 }
150 else {
151 // fill in result from cache
152 res.result = cacheResult;
153 }
154 return undefined;
155 });
156}
157exports.createBlockCacheMiddleware = createBlockCacheMiddleware;
158//# sourceMappingURL=block-cache.js.map
\No newline at end of file