293 lines
6.7 KiB
JavaScript
293 lines
6.7 KiB
JavaScript
var assert = require('assert');
|
|
var encoding = require('../lib/utils/encoding');
|
|
var networks = require('../lib/protocol/networks');
|
|
var co = require('../lib/utils/co');
|
|
var BufferWriter = require('../lib/utils/writer');
|
|
var BufferReader = require('../lib/utils/reader');
|
|
var OldCoins = require('./coins-old');
|
|
var Coins = require('../lib/coins/coins');
|
|
var UndoCoins = require('../lib/coins/undocoins');
|
|
var Coin = require('../lib/primitives/coin');
|
|
var Output = require('../lib/primitives/output');
|
|
var util = require('../lib/utils/util');
|
|
var LDB = require('../lib/db/ldb');
|
|
var file = process.argv[2];
|
|
var options = {};
|
|
var db, batch, index;
|
|
|
|
assert(typeof file === 'string', 'Please pass in a database path.');
|
|
|
|
file = file.replace(/\.ldb\/?$/, '');
|
|
|
|
db = LDB({
|
|
location: file,
|
|
db: 'leveldb',
|
|
compression: true,
|
|
cacheSize: 32 << 20,
|
|
createIfMissing: false,
|
|
bufferKeys: true
|
|
});
|
|
|
|
options = {};
|
|
options.spv = process.argv.indexOf('--spv') !== -1;
|
|
options.prune = process.argv.indexOf('--prune') !== -1;
|
|
options.indexTX = process.argv.indexOf('--index-tx') !== -1;
|
|
options.indexAddress = process.argv.indexOf('--index-address') !== -1;
|
|
options.network = networks.main;
|
|
|
|
index = process.argv.indexOf('--network');
|
|
|
|
if (index !== -1) {
|
|
options.network = networks[process.argv[index + 1]];
|
|
assert(options.network, 'Invalid network.');
|
|
}
|
|
|
|
var updateVersion = co(function* updateVersion() {
|
|
var data, ver;
|
|
|
|
console.log('Checking version.');
|
|
|
|
data = yield db.get('V');
|
|
|
|
if (!data)
|
|
throw new Error('No DB version found!');
|
|
|
|
ver = data.readUInt32LE(0, true);
|
|
|
|
if (ver !== 1)
|
|
throw Error('DB is version ' + ver + '.');
|
|
|
|
ver = new Buffer(4);
|
|
ver.writeUInt32LE(2, 0, true);
|
|
batch.put('V', ver);
|
|
});
|
|
|
|
var checkTipIndex = co(function* checkTipIndex() {
|
|
var keys = yield db.keys({
|
|
gte: pair('p', encoding.ZERO_HASH),
|
|
lte: pair('p', encoding.MAX_HASH)
|
|
});
|
|
|
|
if (keys.length === 0) {
|
|
console.log('No tip index found.');
|
|
console.log('Please run migrate/ensure-tip-index.js first!');
|
|
process.exit(1);
|
|
return;
|
|
}
|
|
|
|
if (keys.length < 3) {
|
|
console.log('Note: please run ensure-tip-index.js if you haven\'t yet.');
|
|
yield co.timeout(2000);
|
|
return;
|
|
}
|
|
});
|
|
|
|
var updateOptions = co(function* updateOptions() {
|
|
if (yield db.has('O'))
|
|
return;
|
|
|
|
if (process.argv.indexOf('--network') === -1) {
|
|
console.log('Warning: no options found in chaindb.');
|
|
console.log('Make sure you selected the correct options');
|
|
console.log('which may include any of:');
|
|
console.log('`--network [name]`, `--spv`, `--witness`,');
|
|
console.log('`--prune`, `--index-tx`, and `--index-address`.');
|
|
console.log('Continuing migration in 5 seconds...');
|
|
yield co.timeout(5000);
|
|
}
|
|
|
|
batch.put('O', defaultOptions());
|
|
});
|
|
|
|
var updateDeployments = co(function* updateDeployments() {
|
|
if (yield db.has('v'))
|
|
return;
|
|
|
|
if (process.argv.indexOf('--network') === -1) {
|
|
console.log('Warning: no deployment table found.');
|
|
console.log('Make sure `--network` is set properly.');
|
|
console.log('Continuing migration in 5 seconds...');
|
|
yield co.timeout(5000);
|
|
}
|
|
|
|
batch.put('v', defaultDeployments());
|
|
});
|
|
|
|
var reserializeCoins = co(function* reserializeCoins() {
|
|
var total = 0;
|
|
var i, iter, item, hash, old, coins, coin, output;
|
|
|
|
iter = db.iterator({
|
|
gte: pair('c', encoding.ZERO_HASH),
|
|
lte: pair('c', encoding.MAX_HASH),
|
|
values: true
|
|
});
|
|
|
|
for (;;) {
|
|
item = yield iter.next();
|
|
|
|
if (!item)
|
|
break;
|
|
|
|
hash = item.key.toString('hex', 1, 33);
|
|
old = OldCoins.fromRaw(item.value, hash);
|
|
|
|
coins = new Coins();
|
|
coins.version = old.version;
|
|
coins.hash = old.hash;
|
|
coins.height = old.height;
|
|
coins.coinbase = old.coinbase;
|
|
|
|
for (i = 0; i < old.outputs.length; i++) {
|
|
coin = old.get(i);
|
|
|
|
if (!coin) {
|
|
coins.outputs.push(null);
|
|
continue;
|
|
}
|
|
|
|
output = new Output();
|
|
output.script = coin.script;
|
|
output.value = coin.value;
|
|
|
|
if (!output.script.isUnspendable())
|
|
coins.addOutput(coin.index, output);
|
|
}
|
|
|
|
coins.cleanup();
|
|
|
|
batch.put(item.key, coins.toRaw());
|
|
|
|
if (++total % 100000 === 0)
|
|
console.log('Reserialized %d coins.', total);
|
|
}
|
|
|
|
console.log('Reserialized %d coins.', total);
|
|
});
|
|
|
|
var reserializeUndo = co(function* reserializeUndo() {
|
|
var total = 0;
|
|
var iter, item, br, undo;
|
|
|
|
iter = db.iterator({
|
|
gte: pair('u', encoding.ZERO_HASH),
|
|
lte: pair('u', encoding.MAX_HASH),
|
|
values: true
|
|
});
|
|
|
|
for (;;) {
|
|
item = yield iter.next();
|
|
|
|
if (!item)
|
|
break;
|
|
|
|
br = new BufferReader(item.value);
|
|
undo = new UndoCoins();
|
|
|
|
while (br.left()) {
|
|
undo.push(null);
|
|
injectCoin(undo.top(), Coin.fromReader(br));
|
|
}
|
|
|
|
batch.put(item.key, undo.toRaw());
|
|
|
|
if (++total % 10000 === 0)
|
|
console.log('Reserialized %d undo coins.', total);
|
|
}
|
|
|
|
console.log('Reserialized %d undo coins.', total);
|
|
});
|
|
|
|
function write(data, str, off) {
|
|
if (Buffer.isBuffer(str))
|
|
return str.copy(data, off);
|
|
data.write(str, off, 'hex');
|
|
}
|
|
|
|
function pair(prefix, hash) {
|
|
var key = new Buffer(33);
|
|
if (typeof prefix === 'string')
|
|
prefix = prefix.charCodeAt(0);
|
|
key[0] = prefix;
|
|
write(key, hash, 1);
|
|
return key;
|
|
}
|
|
|
|
function ipair(prefix, num) {
|
|
var key = new Buffer(5);
|
|
if (typeof prefix === 'string')
|
|
prefix = prefix.charCodeAt(0);
|
|
key[0] = prefix;
|
|
key.writeUInt32BE(num, 1, true);
|
|
return key;
|
|
}
|
|
|
|
function injectCoin(undo, coin) {
|
|
var output = new Output();
|
|
|
|
output.value = coin.value;
|
|
output.script = coin.script;
|
|
|
|
undo.output = output;
|
|
undo.version = coin.version;
|
|
undo.height = coin.height;
|
|
undo.coinbase = coin.coinbase;
|
|
}
|
|
|
|
function defaultOptions() {
|
|
var bw = new BufferWriter();
|
|
var flags = 0;
|
|
|
|
if (options.spv)
|
|
flags |= 1 << 0;
|
|
|
|
flags |= 1 << 1;
|
|
|
|
if (options.prune)
|
|
flags |= 1 << 2;
|
|
|
|
if (options.indexTX)
|
|
flags |= 1 << 3;
|
|
|
|
if (options.indexAddress)
|
|
flags |= 1 << 4;
|
|
|
|
bw.writeU32(options.network.magic);
|
|
bw.writeU32(flags);
|
|
bw.writeU32(0);
|
|
|
|
return bw.render();
|
|
}
|
|
|
|
function defaultDeployments() {
|
|
var bw = new BufferWriter();
|
|
var i, deployment;
|
|
|
|
bw.writeU8(options.network.deploys.length);
|
|
|
|
for (i = 0; i < options.network.deploys.length; i++) {
|
|
deployment = options.network.deploys[i];
|
|
bw.writeU8(deployment.bit);
|
|
bw.writeU32(deployment.startTime);
|
|
bw.writeU32(deployment.timeout);
|
|
}
|
|
|
|
return bw.render();
|
|
}
|
|
|
|
co.spawn(function* () {
|
|
yield db.open();
|
|
console.log('Opened %s.', file);
|
|
batch = db.batch();
|
|
yield updateVersion();
|
|
yield checkTipIndex();
|
|
yield updateOptions();
|
|
yield updateDeployments();
|
|
yield reserializeCoins();
|
|
yield reserializeUndo();
|
|
yield batch.write();
|
|
}).then(function() {
|
|
console.log('Migration complete.');
|
|
process.exit(0);
|
|
});
|