UNPKG

incubed

Version:

Typescript-version of the incubed client

137 lines 6.05 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const ChainContext_1 = require("../../client/ChainContext"); const filter_1 = require("./filter"); const serialize_1 = require("./serialize"); const util_1 = require("../../util/util"); const ethereumjs_util_1 = require("ethereumjs-util"); const Buffer = require('buffer').Buffer; class EthChainContext extends ChainContext_1.default { constructor(client, chainId, chainSpec) { super(client, chainId, chainSpec); this.filters = new filter_1.default(); } handleIntern(request) { return this.filters.handleIntern(request, this.client); } getCodeFor(addresses, block = 'latest') { return __awaiter(this, void 0, void 0, function* () { const result = addresses.map(a => this.codeCache.get(a)); const missing = result.map((_, i) => _ ? null : { method: 'eth_getCode', params: [util_1.toHex(addresses[i], 20), block[0] === 'l' ? block : util_1.toMinHex(block)], id: i + 1, jsonrpc: '2.0' }).filter(_ => _); if (missing.length) { for (const r of yield this.client.send(missing, undefined, { proof: 'none', signatureCount: 0, chainId: this.chainId })) { const i = r.id - 1; if (r.error) throw new Error(' could not get the code for address ' + addresses[i] + ' : ' + r.error); this.codeCache.put(addresses[i], serialize_1.bytes(result[i] = r.result)); } if (this.client.defConfig.cacheStorage && this.chainId) this.client.defConfig.cacheStorage.setItem('in3.code.' + this.chainId, this.codeCache.toStorage()); } return result; }); } getLastBlockHashes() { return this.blockCache.map(_ => util_1.toHex(_.hash)); } getBlockHeader(blockNumber) { const b = this.blockCache.length && this.blockCache.find(_ => _.number === blockNumber); return b ? b.header : null; } getBlockHeaderByHash(blockHash) { const b = this.blockCache.length && this.blockCache.find(_ => _.hash.equals(blockHash)); return b ? b.header : null; } addBlockHeader(blockNumber, header) { if (!this.client.defConfig.maxBlockCache || header.equals(this.getBlockHeader(blockNumber) || Buffer.allocUnsafe(0))) return header; while (this.blockCache.length >= this.client.defConfig.maxBlockCache && this.blockCache.length) this.blockCache.splice(this.blockCache.reduce((p, c, i, a) => c.number < a[i].number ? i : p, 0), 1); this.blockCache.push({ number: blockNumber, header, hash: ethereumjs_util_1.keccak(header) }); this.client.defConfig.verifiedHashes = this.getLastBlockHashes(); return header; } initCache() { super.initCache(); this.codeCache = new CacheNode(this.client.defConfig.maxCodeCache || 100000); this.blockCache = []; const chainId = this.chainId; if (this.client.defConfig.cacheStorage && chainId) { // read codeCache const codeCache = this.client.defConfig.cacheStorage.getItem('in3.code.' + chainId); try { if (codeCache) this.codeCache.fromStorage(codeCache); } catch (ex) { this.client.defConfig.cacheStorage.setItem('in3.code.' + chainId, ''); } } } } exports.default = EthChainContext; class CacheNode { constructor(limit) { this.limit = limit; this.dataLength = 0; this.data = new Map(); } get(key) { const entry = this.data.get(key.toString('hex')); return entry ? entry.data : null; } put(key, val) { const old = this.get(key); if (old) { this.dataLength -= this.getByteLength(old); this.data.delete(key.toString('hex')); } const size = this.getByteLength(val); while (this.limit && !old && this.dataLength + size >= this.limit) { let oldestKey = null; let oldestVal = { added: Date.now(), data: null }; for (const [k, v] of this.data.entries()) { if (v.added < oldestVal.added) { oldestVal = v; oldestKey = k; } } if (!oldestKey) break; this.data.delete(oldestKey); this.dataLength -= this.getByteLength(oldestVal.data); } this.data.set(key.toString('hex'), { added: Date.now(), data: val }); this.dataLength += this.getByteLength(val); } getByteLength(entry) { if (Buffer.isBuffer(entry)) return entry.length; if (typeof entry === 'string') return entry.length * 2; return 4; } toStorage() { const entries = []; this.data.forEach((val, key) => { entries.push(Buffer.from(key, 'hex')); entries.push(val.data); }); return serialize_1.rlp.encode(entries).toString('base64'); } fromStorage(data) { const entries = serialize_1.rlp.decode(Buffer.from(data, 'base64')); for (let i = 0; i < entries.length; i += 2) this.put(entries[i], entries[i + 1]); } } exports.CacheNode = CacheNode; //# sourceMappingURL=EthChainContext.js.map