fcoin/lib/chain/blockdb.js
2016-11-18 18:01:33 -08:00

197 lines
3.7 KiB
JavaScript

/*!
* blockdb.js - blockchain data management for bcoin
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
* Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin
*/
'use strict';
var assert = require('assert');
var co = require('../utils/co');
var Flat = require('../db/flat');
var LRU = require('../utils/lru');
var FileEntry = Flat.FileEntry;
/**
* BlockDB
* @constructor
*/
function BlockDB(chaindb) {
this.chaindb = chaindb;
this.db = chaindb.db;
this.layout = chaindb.layout;
this.flat = new Flat(this.db);
this.cache = new LRU(8192);
}
BlockDB.prototype.open = function open() {
return this.flat.open();
};
BlockDB.prototype.close = function close() {
return this.flat.close();
};
BlockDB.prototype.sync = co(function* sync() {
var entry = yield this.chaindb.getTip();
var block, rollback;
assert(entry);
for (;;) {
try {
block = yield this.readBlock(entry.hash);
} catch (e) {
if (e.type !== 'ChecksumMismatch')
throw e;
block = null;
}
if (block)
break;
this.cache.remove(entry.hash);
entry = yield entry.getPrevious();
assert(entry);
rollback = true;
}
if (!rollback)
return;
yield this.chaindb.reset(entry.hash, true);
});
BlockDB.prototype.getEntry = co(function* getEntry(hash) {
var key = hash;
var entry, data;
if (typeof key !== 'string')
key = key.toString('hex');
entry = this.cache.get(key);
if (entry)
return entry;
data = yield this.db.get(this.layout.b(hash));
if (!data)
return;
entry = FileEntry.fromRaw(data);
this.cache.set(key, entry);
return entry;
});
BlockDB.prototype.saveBlock = co(function* saveBlock(block) {
var hash = block.hash();
var hex = block.hash('hex');
var entry = yield this.flat.write(block.toRaw());
if (block.height === 0)
yield this.flat.sync();
this.cache.set(hex, entry);
this.chaindb.put(this.layout.b(hash), entry.toRaw());
});
BlockDB.prototype.readBlock = co(function* readBlock(hash) {
var entry = yield this.getEntry(hash);
if (!entry)
return;
return yield this.readBlockEntry(entry);
});
BlockDB.prototype.readBlockEntry = function readBlockEntry(entry) {
return this.flat.read(entry.index, entry.pos);
};
BlockDB.prototype.removeBlock = co(function* removeBlock(hash) {
var entry = yield this.getEntry(hash);
if (!entry)
return;
this.chaindb.del(this.layout.b(hash));
if (entry.pos === 0)
yield this.flat.remove(entry.index);
});
BlockDB.prototype.pruneBlock = co(function* pruneBlock(hash) {
var entry = yield this.getEntry(hash);
if (!entry)
return;
return yield this.pruneBlockEntry(hash, entry);
});
BlockDB.prototype.pruneBlockEntry = function pruneBlockEntry(hash, entry) {
var index = entry.index;
if (index === this.current.index)
index -= 1;
this.chaindb.del(this.layout.b(hash));
return this.flat.remove(index);
};
/**
* Batch
* @constructor
*/
function Batch(ffdb) {
this.ffdb = ffdb;
this.ops = [];
}
Batch.prototype.put = function put(block) {
this.ops.push(new BatchOp(0, block));
};
Batch.prototype.del = function del(hash) {
this.ops.push(new BatchOp(1, hash));
};
Batch.prototype.write = co(function* write() {
var i, op;
for (i = 0; i < this.ops.length; i++) {
op = this.ops[i];
switch (op.type) {
case 0:
yield this.ffdb.saveBlock(op.data);
break;
case 1:
yield this.ffdb.removeBlock(op.data);
break;
default:
assert(false);
}
}
});
/**
* BatchOp
* @constructor
*/
function BatchOp(type, data) {
this.type = type;
this.data = data;
}
/*
* Expose
*/
module.exports = BlockDB;