incubed
Version:
Typescript-version of the incubed client
137 lines • 6.05 kB
JavaScript
"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