db: drop old migrations. avoid using encoding.
This commit is contained in:
parent
83e1de2e98
commit
cb978df380
@ -1,11 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
const random = require('bcrypto/lib/random');
|
||||
const Address = require('../lib/primitives/address');
|
||||
const TX = require('../lib/primitives/tx');
|
||||
const Script = require('../lib/script/script');
|
||||
const MTX = require('../lib/primitives/mtx');
|
||||
const encoding = require('../lib/utils/encoding');
|
||||
const random = require('bcrypto/lib/random');
|
||||
const consensus = require('../lib/protocol/consensus');
|
||||
const common = require('../test/util/common');
|
||||
const bench = require('./bench');
|
||||
|
||||
@ -156,7 +156,7 @@ const mtx = new MTX();
|
||||
for (let i = 0; i < 100; i++) {
|
||||
mtx.addInput({
|
||||
prevout: {
|
||||
hash: encoding.NULL_HASH,
|
||||
hash: consensus.NULL_HASH,
|
||||
index: 0
|
||||
},
|
||||
script: new Script()
|
||||
|
||||
@ -17,13 +17,13 @@ const UndoCoins = require('../coins/undocoins');
|
||||
const layout = require('./layout');
|
||||
const LRU = require('../utils/lru');
|
||||
const util = require('../utils/util');
|
||||
const consensus = require('../protocol/consensus');
|
||||
const Block = require('../primitives/block');
|
||||
const Outpoint = require('../primitives/outpoint');
|
||||
const Address = require('../primitives/address');
|
||||
const ChainEntry = require('./chainentry');
|
||||
const TXMeta = require('../primitives/txmeta');
|
||||
const CoinEntry = require('../coins/coinentry');
|
||||
const {encoding} = bio;
|
||||
|
||||
/**
|
||||
* ChainDB
|
||||
@ -255,7 +255,7 @@ class ChainDB {
|
||||
|
||||
assert(typeof hash === 'string');
|
||||
|
||||
if (hash === encoding.NULL_HASH)
|
||||
if (hash === consensus.NULL_HASH)
|
||||
return -1;
|
||||
|
||||
const entry = this.cacheHash.get(hash);
|
||||
@ -348,7 +348,7 @@ class ChainDB {
|
||||
async getEntryByHash(hash) {
|
||||
assert(typeof hash === 'string');
|
||||
|
||||
if (hash === encoding.NULL_HASH)
|
||||
if (hash === consensus.NULL_HASH)
|
||||
return null;
|
||||
|
||||
const cache = this.cacheHash.get(hash);
|
||||
@ -827,7 +827,7 @@ class ChainDB {
|
||||
async isMainHash(hash) {
|
||||
assert(typeof hash === 'string');
|
||||
|
||||
if (hash === encoding.NULL_HASH)
|
||||
if (hash === consensus.NULL_HASH)
|
||||
return false;
|
||||
|
||||
if (hash === this.network.genesis.hash)
|
||||
@ -2129,7 +2129,7 @@ class ChainState {
|
||||
*/
|
||||
|
||||
constructor() {
|
||||
this.tip = encoding.NULL_HASH;
|
||||
this.tip = consensus.NULL_HASH;
|
||||
this.tx = 0;
|
||||
this.coin = 0;
|
||||
this.value = 0;
|
||||
|
||||
@ -15,7 +15,6 @@ const hash256 = require('bcrypto/lib/hash256');
|
||||
const util = require('../utils/util');
|
||||
const Headers = require('../primitives/headers');
|
||||
const InvItem = require('../primitives/invitem');
|
||||
const {encoding} = bio;
|
||||
|
||||
/*
|
||||
* Constants
|
||||
@ -51,10 +50,10 @@ class ChainEntry {
|
||||
*/
|
||||
|
||||
constructor(options) {
|
||||
this.hash = encoding.NULL_HASH;
|
||||
this.hash = consensus.NULL_HASH;
|
||||
this.version = 1;
|
||||
this.prevBlock = encoding.NULL_HASH;
|
||||
this.merkleRoot = encoding.NULL_HASH;
|
||||
this.prevBlock = consensus.NULL_HASH;
|
||||
this.merkleRoot = consensus.NULL_HASH;
|
||||
this.time = 0;
|
||||
this.bits = 0;
|
||||
this.nonce = 0;
|
||||
|
||||
@ -16,10 +16,10 @@ const cleanse = require('bcrypto/lib/cleanse');
|
||||
const random = require('bcrypto/lib/random');
|
||||
const secp256k1 = require('bcrypto/lib/secp256k1');
|
||||
const Network = require('../protocol/network');
|
||||
const consensus = require('../protocol/consensus');
|
||||
const common = require('./common');
|
||||
const Mnemonic = require('./mnemonic');
|
||||
const HDPublicKey = require('./public');
|
||||
const {encoding} = bio;
|
||||
|
||||
/*
|
||||
* Constants
|
||||
@ -53,8 +53,8 @@ class HDPrivateKey {
|
||||
this.depth = 0;
|
||||
this.parentFingerPrint = 0;
|
||||
this.childIndex = 0;
|
||||
this.chainCode = encoding.ZERO_HASH;
|
||||
this.privateKey = encoding.ZERO_HASH;
|
||||
this.chainCode = consensus.ZERO_HASH;
|
||||
this.privateKey = consensus.ZERO_HASH;
|
||||
|
||||
this.publicKey = common.ZERO_KEY;
|
||||
this.fingerPrint = -1;
|
||||
|
||||
@ -15,8 +15,8 @@ const hash256 = require('bcrypto/lib/hash256');
|
||||
const cleanse = require('bcrypto/lib/cleanse');
|
||||
const secp256k1 = require('bcrypto/lib/secp256k1');
|
||||
const Network = require('../protocol/network');
|
||||
const consensus = require('../protocol/consensus');
|
||||
const common = require('./common');
|
||||
const {encoding} = bio;
|
||||
|
||||
/**
|
||||
* HDPublicKey
|
||||
@ -45,7 +45,7 @@ class HDPublicKey {
|
||||
this.depth = 0;
|
||||
this.parentFingerPrint = 0;
|
||||
this.childIndex = 0;
|
||||
this.chainCode = encoding.ZERO_HASH;
|
||||
this.chainCode = consensus.ZERO_HASH;
|
||||
this.publicKey = common.ZERO_KEY;
|
||||
|
||||
this.fingerPrint = -1;
|
||||
|
||||
@ -11,12 +11,14 @@ const bdb = require('bdb');
|
||||
/*
|
||||
* Database Layout:
|
||||
* V -> db version
|
||||
* v -> serialization version
|
||||
* R -> tip hash
|
||||
* e[hash] -> entry
|
||||
*/
|
||||
|
||||
const layout = {
|
||||
V: bdb.key('v'),
|
||||
V: bdb.key('V'),
|
||||
v: bdb.key('v'),
|
||||
R: bdb.key('R'),
|
||||
F: bdb.key('F'),
|
||||
e: bdb.key('e', ['hash256'])
|
||||
|
||||
@ -2389,7 +2389,7 @@ class MempoolCache {
|
||||
}
|
||||
|
||||
async getVersion() {
|
||||
const data = await this.db.get(layout.V.build());
|
||||
const data = await this.db.get(layout.v.build());
|
||||
|
||||
if (!data)
|
||||
return -1;
|
||||
@ -2412,7 +2412,8 @@ class MempoolCache {
|
||||
if (!data)
|
||||
return null;
|
||||
|
||||
let fees;
|
||||
let fees = null;
|
||||
|
||||
try {
|
||||
fees = Fees.fromRaw(data);
|
||||
} catch (e) {
|
||||
@ -2444,6 +2445,8 @@ class MempoolCache {
|
||||
return;
|
||||
|
||||
await this.db.open();
|
||||
await this.db.verify(layout.V.build(), 'mempool', 0);
|
||||
|
||||
await this.verify();
|
||||
|
||||
this.batch = this.db.batch();
|
||||
@ -2502,7 +2505,7 @@ class MempoolCache {
|
||||
|
||||
async init(hash) {
|
||||
const batch = this.db.batch();
|
||||
batch.put(layout.V.build(), encoding.u32(MempoolCache.VERSION));
|
||||
batch.put(layout.v.build(), encoding.u32(MempoolCache.VERSION));
|
||||
batch.put(layout.R.build(), Buffer.from(hash, 'hex'));
|
||||
await batch.write();
|
||||
}
|
||||
@ -2554,7 +2557,7 @@ class MempoolCache {
|
||||
for (const key of keys)
|
||||
batch.del(key);
|
||||
|
||||
batch.put(layout.V.build(), encoding.u32(MempoolCache.VERSION));
|
||||
batch.put(layout.v.build(), encoding.u32(MempoolCache.VERSION));
|
||||
batch.put(layout.R.build(), Buffer.from(this.chain.tip.hash, 'hex'));
|
||||
batch.del(layout.F.build());
|
||||
|
||||
|
||||
@ -22,7 +22,11 @@ const policy = require('../protocol/policy');
|
||||
const CoinView = require('../coins/coinview');
|
||||
const Script = require('../script/script');
|
||||
const common = require('./common');
|
||||
const {encoding} = bio;
|
||||
|
||||
/*
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const DUMMY = Buffer.alloc(0);
|
||||
|
||||
/**
|
||||
@ -38,12 +42,12 @@ class BlockTemplate {
|
||||
*/
|
||||
|
||||
constructor(options) {
|
||||
this.prevBlock = encoding.NULL_HASH;
|
||||
this.prevBlock = consensus.NULL_HASH;
|
||||
this.version = 1;
|
||||
this.height = 0;
|
||||
this.time = 0;
|
||||
this.bits = 0;
|
||||
this.target = encoding.ZERO_HASH;
|
||||
this.target = consensus.ZERO_HASH;
|
||||
this.locktime = 0;
|
||||
this.mtp = 0;
|
||||
this.flags = 0;
|
||||
@ -55,7 +59,7 @@ class BlockTemplate {
|
||||
this.interval = 210000;
|
||||
this.fees = 0;
|
||||
this.tree = new MerkleTree();
|
||||
this.commitment = encoding.ZERO_HASH;
|
||||
this.commitment = consensus.ZERO_HASH;
|
||||
this.left = DUMMY;
|
||||
this.right = DUMMY;
|
||||
this.items = [];
|
||||
@ -172,10 +176,10 @@ class BlockTemplate {
|
||||
*/
|
||||
|
||||
getWitnessHash() {
|
||||
const nonce = encoding.ZERO_HASH;
|
||||
const nonce = consensus.ZERO_HASH;
|
||||
const leaves = [];
|
||||
|
||||
leaves.push(encoding.ZERO_HASH);
|
||||
leaves.push(consensus.ZERO_HASH);
|
||||
|
||||
for (const {tx} of this.items)
|
||||
leaves.push(tx.witnessHash());
|
||||
@ -260,7 +264,7 @@ class BlockTemplate {
|
||||
|
||||
// Set up the witness nonce.
|
||||
if (this.witness) {
|
||||
input.witness.push(encoding.ZERO_HASH);
|
||||
input.witness.push(consensus.ZERO_HASH);
|
||||
input.witness.compile();
|
||||
}
|
||||
|
||||
@ -439,7 +443,7 @@ class BlockTemplate {
|
||||
|
||||
if (this.witness) {
|
||||
const input = tx.inputs[0];
|
||||
input.witness.push(encoding.ZERO_HASH);
|
||||
input.witness.push(consensus.ZERO_HASH);
|
||||
input.witness.compile();
|
||||
tx.refresh();
|
||||
}
|
||||
@ -723,7 +727,7 @@ class MerkleTree {
|
||||
fromItems(items) {
|
||||
const leaves = [];
|
||||
|
||||
leaves.push(encoding.ZERO_HASH);
|
||||
leaves.push(consensus.ZERO_HASH);
|
||||
|
||||
for (const item of items)
|
||||
leaves.push(item.tx.hash());
|
||||
@ -738,7 +742,7 @@ class MerkleTree {
|
||||
fromBlock(txs) {
|
||||
const leaves = [];
|
||||
|
||||
leaves.push(encoding.ZERO_HASH);
|
||||
leaves.push(consensus.ZERO_HASH);
|
||||
|
||||
for (let i = 1; i < txs.length; i++) {
|
||||
const tx = txs[i];
|
||||
@ -756,7 +760,7 @@ class MerkleTree {
|
||||
let len = leaves.length;
|
||||
|
||||
while (len > 1) {
|
||||
const hashes = [encoding.ZERO_HASH];
|
||||
const hashes = [consensus.ZERO_HASH];
|
||||
|
||||
this.steps.push(leaves[1]);
|
||||
|
||||
|
||||
@ -22,9 +22,9 @@ const hash160 = require('bcrypto/lib/hash160');
|
||||
const hash256 = require('bcrypto/lib/hash256');
|
||||
const random = require('bcrypto/lib/random');
|
||||
const secp256k1 = require('bcrypto/lib/secp256k1');
|
||||
const consensus = require('../protocol/consensus');
|
||||
const packets = require('./packets');
|
||||
const common = require('./common');
|
||||
const {encoding} = bio;
|
||||
|
||||
/**
|
||||
* BIP150
|
||||
@ -129,7 +129,7 @@ class BIP150 extends EventEmitter {
|
||||
assert(!this.challengeReceived, 'Peer challenged twice.');
|
||||
this.challengeReceived = true;
|
||||
|
||||
if (hash.equals(encoding.ZERO_HASH))
|
||||
if (hash.equals(consensus.ZERO_HASH))
|
||||
throw new Error('Auth failure.');
|
||||
|
||||
const msg = this.hash(this.input.sid, type, this.publicKey);
|
||||
@ -205,7 +205,7 @@ class BIP150 extends EventEmitter {
|
||||
const match = this.findAuthorized(hash);
|
||||
|
||||
if (!match)
|
||||
return encoding.ZERO_HASH;
|
||||
return consensus.ZERO_HASH;
|
||||
|
||||
this.peerIdentity = match;
|
||||
|
||||
|
||||
@ -554,7 +554,7 @@ class TXRequest {
|
||||
*/
|
||||
|
||||
constructor(options) {
|
||||
this.hash = encoding.NULL_HASH;
|
||||
this.hash = consensus.NULL_HASH;
|
||||
this.indexes = [];
|
||||
|
||||
if (options)
|
||||
@ -749,7 +749,7 @@ class TXResponse {
|
||||
*/
|
||||
|
||||
constructor(options) {
|
||||
this.hash = encoding.NULL_HASH;
|
||||
this.hash = consensus.NULL_HASH;
|
||||
this.txs = [];
|
||||
|
||||
if (options)
|
||||
|
||||
@ -18,6 +18,7 @@ const common = require('./common');
|
||||
const util = require('../utils/util');
|
||||
const bip152 = require('./bip152');
|
||||
const NetAddress = require('./netaddress');
|
||||
const consensus = require('../protocol/consensus');
|
||||
const Headers = require('../primitives/headers');
|
||||
const InvItem = require('../primitives/invitem');
|
||||
const MemBlock = require('../primitives/memblock');
|
||||
@ -1019,7 +1020,7 @@ class GetBlocksPacket extends Packet {
|
||||
for (const hash of this.locator)
|
||||
bw.writeHash(hash);
|
||||
|
||||
bw.writeHash(this.stop || encoding.ZERO_HASH);
|
||||
bw.writeHash(this.stop || consensus.ZERO_HASH);
|
||||
|
||||
return bw;
|
||||
}
|
||||
@ -1052,7 +1053,7 @@ class GetBlocksPacket extends Packet {
|
||||
|
||||
this.stop = br.readHash('hex');
|
||||
|
||||
if (this.stop === encoding.NULL_HASH)
|
||||
if (this.stop === consensus.NULL_HASH)
|
||||
this.stop = null;
|
||||
|
||||
return this;
|
||||
@ -2853,7 +2854,7 @@ class AuthChallengePacket extends Packet {
|
||||
this.cmd = 'authchallenge';
|
||||
this.type = exports.types.AUTHCHALLENGE;
|
||||
|
||||
this.hash = hash || encoding.ZERO_HASH;
|
||||
this.hash = hash || consensus.ZERO_HASH;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3043,7 +3044,7 @@ class AuthProposePacket extends Packet {
|
||||
this.cmd = 'authpropose';
|
||||
this.type = exports.types.AUTHPROPOSE;
|
||||
|
||||
this.hash = hash || encoding.ZERO_HASH;
|
||||
this.hash = hash || consensus.ZERO_HASH;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -11,7 +11,6 @@ const bweb = require('bweb');
|
||||
const {Lock} = require('bmutex');
|
||||
const IP = require('binet');
|
||||
const Validator = require('bval');
|
||||
const {encoding} = require('bufio');
|
||||
const hash160 = require('bcrypto/lib/hash160');
|
||||
const hash256 = require('bcrypto/lib/hash256');
|
||||
const ccmp = require('bcrypto/lib/ccmp');
|
||||
@ -2432,7 +2431,7 @@ class RPC extends RPCBase {
|
||||
this.logger.debug(tx);
|
||||
|
||||
// Recreate witness nonce (all zeroes).
|
||||
input.witness.push(encoding.ZERO_HASH);
|
||||
input.witness.push(consensus.ZERO_HASH);
|
||||
input.witness.compile();
|
||||
|
||||
tx.refresh();
|
||||
@ -2705,7 +2704,7 @@ class RPC extends RPCBase {
|
||||
bits: entry.bits,
|
||||
difficulty: toDifficulty(entry.bits),
|
||||
chainwork: entry.chainwork.toString('hex', 64),
|
||||
previousblockhash: entry.prevBlock !== encoding.NULL_HASH
|
||||
previousblockhash: entry.prevBlock !== consensus.NULL_HASH
|
||||
? util.revHex(entry.prevBlock)
|
||||
: null,
|
||||
nextblockhash: next ? util.revHex(next) : null
|
||||
@ -2743,7 +2742,7 @@ class RPC extends RPCBase {
|
||||
bits: entry.bits,
|
||||
difficulty: toDifficulty(entry.bits),
|
||||
chainwork: entry.chainwork.toString('hex', 64),
|
||||
previousblockhash: entry.prevBlock !== encoding.NULL_HASH
|
||||
previousblockhash: entry.prevBlock !== consensus.NULL_HASH
|
||||
? util.revHex(entry.prevBlock)
|
||||
: null,
|
||||
nextblockhash: next ? util.revHex(next) : null
|
||||
|
||||
@ -13,7 +13,6 @@ const bio = require('bufio');
|
||||
const util = require('../utils/util');
|
||||
const InvItem = require('./invitem');
|
||||
const consensus = require('../protocol/consensus');
|
||||
const {encoding} = bio;
|
||||
|
||||
/**
|
||||
* Abstract Block
|
||||
@ -36,8 +35,8 @@ class AbstractBlock {
|
||||
|
||||
constructor() {
|
||||
this.version = 1;
|
||||
this.prevBlock = encoding.NULL_HASH;
|
||||
this.merkleRoot = encoding.NULL_HASH;
|
||||
this.prevBlock = consensus.NULL_HASH;
|
||||
this.merkleRoot = consensus.NULL_HASH;
|
||||
this.time = 0;
|
||||
this.bits = 0;
|
||||
this.nonce = 0;
|
||||
|
||||
@ -14,7 +14,7 @@ const sha256 = require('bcrypto/lib/sha256');
|
||||
const hash160 = require('bcrypto/lib/hash160');
|
||||
const hash256 = require('bcrypto/lib/hash256');
|
||||
const Network = require('../protocol/network');
|
||||
const {encoding} = bio;
|
||||
const consensus = require('../protocol/consensus');
|
||||
|
||||
/*
|
||||
* Constants
|
||||
@ -96,7 +96,7 @@ class Address {
|
||||
return this.hash.equals(ZERO_HASH160);
|
||||
|
||||
if (this.hash.length === 32)
|
||||
return this.hash.equals(encoding.ZERO_HASH);
|
||||
return this.hash.equals(consensus.ZERO_HASH);
|
||||
|
||||
for (let i = 0; i < this.hash.length; i++) {
|
||||
if (this.hash[i] !== 0)
|
||||
|
||||
@ -294,7 +294,7 @@ class Block extends AbstractBlock {
|
||||
*/
|
||||
|
||||
createWitnessNonce() {
|
||||
return Buffer.from(encoding.ZERO_HASH);
|
||||
return Buffer.from(consensus.ZERO_HASH);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -310,7 +310,7 @@ class Block extends AbstractBlock {
|
||||
|
||||
assert(nonce, 'No witness nonce present.');
|
||||
|
||||
leaves.push(encoding.ZERO_HASH);
|
||||
leaves.push(consensus.ZERO_HASH);
|
||||
|
||||
for (let i = 1; i < this.txs.length; i++) {
|
||||
const tx = this.txs[i];
|
||||
|
||||
@ -13,7 +13,7 @@ const util = require('../utils/util');
|
||||
const Amount = require('../btc/amount');
|
||||
const Output = require('./output');
|
||||
const Network = require('../protocol/network');
|
||||
const {encoding} = bio;
|
||||
const consensus = require('../protocol/consensus');
|
||||
|
||||
/**
|
||||
* Coin
|
||||
@ -42,7 +42,7 @@ class Coin extends Output {
|
||||
this.version = 1;
|
||||
this.height = -1;
|
||||
this.coinbase = false;
|
||||
this.hash = encoding.NULL_HASH;
|
||||
this.hash = consensus.NULL_HASH;
|
||||
this.index = 0;
|
||||
|
||||
if (options)
|
||||
|
||||
@ -203,7 +203,7 @@ class MerkleBlock extends AbstractBlock {
|
||||
const traverse = (height, pos) => {
|
||||
if (bitsUsed >= flags.length * 8) {
|
||||
failed = true;
|
||||
return encoding.ZERO_HASH;
|
||||
return consensus.ZERO_HASH;
|
||||
}
|
||||
|
||||
const parent = (flags[bitsUsed / 8 | 0] >>> (bitsUsed % 8)) & 1;
|
||||
@ -213,7 +213,7 @@ class MerkleBlock extends AbstractBlock {
|
||||
if (height === 0 || !parent) {
|
||||
if (hashUsed >= hashes.length) {
|
||||
failed = true;
|
||||
return encoding.ZERO_HASH;
|
||||
return consensus.ZERO_HASH;
|
||||
}
|
||||
|
||||
const hash = hashes[hashUsed];
|
||||
@ -659,7 +659,7 @@ class MerkleBlock extends AbstractBlock {
|
||||
|
||||
class PartialTree {
|
||||
constructor(root, matches, indexes, map) {
|
||||
this.root = root ? root.toString('hex') : encoding.NULL_HASH;
|
||||
this.root = root ? root.toString('hex') : consensus.NULL_HASH;
|
||||
this.matches = matches || [];
|
||||
this.indexes = indexes || [];
|
||||
this.map = map || new Map();
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
const assert = require('assert');
|
||||
const bio = require('bufio');
|
||||
const util = require('../utils/util');
|
||||
const {encoding} = bio;
|
||||
const consensus = require('../protocol/consensus');
|
||||
|
||||
/**
|
||||
* Outpoint
|
||||
@ -28,7 +28,7 @@ class Outpoint {
|
||||
*/
|
||||
|
||||
constructor(hash, index) {
|
||||
this.hash = encoding.NULL_HASH;
|
||||
this.hash = consensus.NULL_HASH;
|
||||
this.index = 0xffffffff;
|
||||
|
||||
if (hash != null) {
|
||||
@ -112,7 +112,7 @@ class Outpoint {
|
||||
*/
|
||||
|
||||
isNull() {
|
||||
return this.index === 0xffffffff && this.hash === encoding.NULL_HASH;
|
||||
return this.index === 0xffffffff && this.hash === consensus.NULL_HASH;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -609,9 +609,9 @@ class TX {
|
||||
|
||||
signatureHashV1(index, prev, value, type) {
|
||||
const input = this.inputs[index];
|
||||
let prevouts = encoding.ZERO_HASH;
|
||||
let sequences = encoding.ZERO_HASH;
|
||||
let outputs = encoding.ZERO_HASH;
|
||||
let prevouts = consensus.ZERO_HASH;
|
||||
let sequences = consensus.ZERO_HASH;
|
||||
let outputs = consensus.ZERO_HASH;
|
||||
|
||||
if (!(type & hashType.ANYONECANPAY)) {
|
||||
if (this._hashPrevouts) {
|
||||
|
||||
@ -226,6 +226,23 @@ exports.MAX_MULTISIG_PUBKEYS = 20;
|
||||
|
||||
exports.BIP16_TIME = 1333238400;
|
||||
|
||||
/**
|
||||
* A hash of all zeroes.
|
||||
* @const {Buffer}
|
||||
* @default
|
||||
*/
|
||||
|
||||
exports.ZERO_HASH = Buffer.alloc(32, 0x00);
|
||||
|
||||
/**
|
||||
* A hash of all zeroes.
|
||||
* @const {String}
|
||||
* @default
|
||||
*/
|
||||
|
||||
exports.NULL_HASH =
|
||||
'0000000000000000000000000000000000000000000000000000000000000000';
|
||||
|
||||
/**
|
||||
* Convert a compact number to a big number.
|
||||
* Used for `block.bits` -> `target` conversion.
|
||||
|
||||
@ -14,7 +14,7 @@ const assert = require('assert');
|
||||
const bio = require('bufio');
|
||||
const util = require('../utils/util');
|
||||
const TX = require('../primitives/tx');
|
||||
const {encoding} = bio;
|
||||
const consensus = require('../protocol/consensus');
|
||||
|
||||
/**
|
||||
* Chain State
|
||||
@ -28,7 +28,7 @@ class ChainState {
|
||||
|
||||
constructor() {
|
||||
this.startHeight = 0;
|
||||
this.startHash = encoding.NULL_HASH;
|
||||
this.startHash = consensus.NULL_HASH;
|
||||
this.height = 0;
|
||||
this.marked = false;
|
||||
}
|
||||
@ -106,7 +106,7 @@ class BlockMeta {
|
||||
*/
|
||||
|
||||
constructor(hash, height, time) {
|
||||
this.hash = hash || encoding.NULL_HASH;
|
||||
this.hash = hash || consensus.NULL_HASH;
|
||||
this.height = height != null ? height : -1;
|
||||
this.time = time || 0;
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@ const bweb = require('bweb');
|
||||
const {Lock} = require('bmutex');
|
||||
const fs = require('bfile');
|
||||
const Validator = require('bval');
|
||||
const {encoding} = require('bufio');
|
||||
const hash256 = require('bcrypto/lib/hash256');
|
||||
const util = require('../utils/util');
|
||||
const Amount = require('../btc/amount');
|
||||
@ -24,6 +23,7 @@ const MTX = require('../primitives/mtx');
|
||||
const Outpoint = require('../primitives/outpoint');
|
||||
const Output = require('../primitives/output');
|
||||
const TX = require('../primitives/tx');
|
||||
const consensus = require('../protocol/consensus');
|
||||
const pkg = require('../pkg');
|
||||
const common = require('./common');
|
||||
const RPCBase = bweb.RPC;
|
||||
@ -1064,7 +1064,7 @@ class RPC extends RPCBase {
|
||||
transactions: out,
|
||||
lastblock: highest && highest.block
|
||||
? util.revHex(highest.block)
|
||||
: encoding.NULL_HASH
|
||||
: consensus.NULL_HASH
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -16,8 +16,8 @@ const Coin = require('../primitives/coin');
|
||||
const Outpoint = require('../primitives/outpoint');
|
||||
const records = require('./records');
|
||||
const layout = require('./layout').txdb;
|
||||
const consensus = require('../protocol/consensus');
|
||||
const policy = require('../protocol/policy');
|
||||
const {encoding} = bio;
|
||||
const {TXRecord} = records;
|
||||
|
||||
/**
|
||||
@ -2556,7 +2556,7 @@ class BlockRecord {
|
||||
*/
|
||||
|
||||
constructor(hash, height, time) {
|
||||
this.hash = hash || encoding.NULL_HASH;
|
||||
this.hash = hash || consensus.NULL_HASH;
|
||||
this.height = height != null ? height : -1;
|
||||
this.time = time || 0;
|
||||
this.hashes = new Set();
|
||||
|
||||
@ -60,7 +60,7 @@ class Wallet extends EventEmitter {
|
||||
this.id = null;
|
||||
this.watchOnly = false;
|
||||
this.accountDepth = 0;
|
||||
this.token = encoding.ZERO_HASH;
|
||||
this.token = consensus.ZERO_HASH;
|
||||
this.tokenDepth = 0;
|
||||
this.master = new MasterKey();
|
||||
|
||||
|
||||
@ -1,110 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const bcoin = require('../');
|
||||
const assert = require('assert');
|
||||
const bio = require('bufio');
|
||||
|
||||
let file = process.argv[2];
|
||||
|
||||
assert(typeof file === 'string', 'Please pass in a database path.');
|
||||
|
||||
file = file.replace(/\.ldb\/?$/, '');
|
||||
|
||||
const db = bcoin.ldb({
|
||||
location: file,
|
||||
db: 'leveldb',
|
||||
compression: true,
|
||||
cacheSize: 32 << 20,
|
||||
createIfMissing: false,
|
||||
bufferKeys: true
|
||||
});
|
||||
|
||||
function makeKey(data) {
|
||||
const height = data.readUInt32LE(1, true);
|
||||
const key = Buffer.allocUnsafe(5);
|
||||
key[0] = 0x48;
|
||||
key.writeUInt32BE(height, 1, true);
|
||||
return key;
|
||||
}
|
||||
|
||||
async function checkVersion() {
|
||||
console.log('Checking version.');
|
||||
|
||||
const data = await db.get('V');
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
const ver = data.readUInt32LE(0, true);
|
||||
|
||||
if (ver !== 0)
|
||||
throw Error(`DB is version ${ver}.`);
|
||||
}
|
||||
|
||||
async function updateState() {
|
||||
console.log('Updating chain state.');
|
||||
|
||||
const data = await db.get('R');
|
||||
|
||||
if (!data || data.length < 32)
|
||||
throw new Error('No chain state.');
|
||||
|
||||
const hash = data.slice(0, 32);
|
||||
|
||||
let p = bio.write();
|
||||
p.writeHash(hash);
|
||||
p.writeU64(0);
|
||||
p.writeU64(0);
|
||||
p.writeU64(0);
|
||||
p = p.render();
|
||||
|
||||
const batch = db.batch();
|
||||
|
||||
batch.put('R', p);
|
||||
|
||||
const ver = Buffer.allocUnsafe(4);
|
||||
ver.writeUInt32LE(1, 0, true);
|
||||
batch.put('V', ver);
|
||||
|
||||
await batch.write();
|
||||
|
||||
console.log('Updated chain state.');
|
||||
}
|
||||
|
||||
async function updateEndian() {
|
||||
const batch = db.batch();
|
||||
let total = 0;
|
||||
|
||||
console.log('Updating endianness.');
|
||||
console.log('Iterating...');
|
||||
|
||||
const iter = db.iterator({
|
||||
gte: Buffer.from('4800000000', 'hex'),
|
||||
lte: Buffer.from('48ffffffff', 'hex'),
|
||||
values: true
|
||||
});
|
||||
|
||||
while (await iter.next()) {
|
||||
const {key, value} = iter;
|
||||
batch.del(key);
|
||||
batch.put(makeKey(key), value);
|
||||
total++;
|
||||
}
|
||||
|
||||
console.log('Migrating %d items.', total);
|
||||
|
||||
await batch.write();
|
||||
|
||||
console.log('Migrated endianness.');
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await db.open();
|
||||
console.log('Opened %s.', file);
|
||||
await checkVersion();
|
||||
await updateState();
|
||||
await updateEndian();
|
||||
})().then(() => {
|
||||
console.log('Migration complete.');
|
||||
process.exit(0);
|
||||
});
|
||||
@ -1,274 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const BDB = require('bdb');
|
||||
const bio = require('bufio');
|
||||
const networks = require('../lib/protocol/networks');
|
||||
const OldCoins = require('./coins-old');
|
||||
const Coins = require('../lib/coins/coins');
|
||||
const UndoCoins = require('../lib/coins/undocoins');
|
||||
const Coin = require('../lib/primitives/coin');
|
||||
const Output = require('../lib/primitives/output');
|
||||
const {encoding} = bio;
|
||||
|
||||
let file = process.argv[2];
|
||||
let batch;
|
||||
|
||||
assert(typeof file === 'string', 'Please pass in a database path.');
|
||||
|
||||
file = file.replace(/\.ldb\/?$/, '');
|
||||
|
||||
const db = new BDB({
|
||||
location: file,
|
||||
db: 'leveldb',
|
||||
compression: true,
|
||||
cacheSize: 32 << 20,
|
||||
createIfMissing: false,
|
||||
bufferKeys: true
|
||||
});
|
||||
|
||||
const 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;
|
||||
|
||||
const index = process.argv.indexOf('--network');
|
||||
|
||||
if (index !== -1) {
|
||||
options.network = networks[process.argv[index + 1]];
|
||||
assert(options.network, 'Invalid network.');
|
||||
}
|
||||
|
||||
async function updateVersion() {
|
||||
console.log('Checking version.');
|
||||
|
||||
const data = await db.get('V');
|
||||
|
||||
if (!data)
|
||||
throw new Error('No DB version found!');
|
||||
|
||||
let ver = data.readUInt32LE(0, true);
|
||||
|
||||
if (ver !== 1)
|
||||
throw Error(`DB is version ${ver}.`);
|
||||
|
||||
ver = Buffer.allocUnsafe(4);
|
||||
ver.writeUInt32LE(2, 0, true);
|
||||
batch.put('V', ver);
|
||||
}
|
||||
|
||||
async function checkTipIndex() {
|
||||
const keys = await 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 undefined;
|
||||
}
|
||||
|
||||
if (keys.length < 3) {
|
||||
console.log('Note: please run ensure-tip-index.js if you haven\'t yet.');
|
||||
return new Promise(r => setTimeout(r, 2000));
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async function updateOptions() {
|
||||
if (await 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...');
|
||||
await new Promise(r => setTimeout(r, 5000));
|
||||
}
|
||||
|
||||
batch.put('O', defaultOptions());
|
||||
}
|
||||
|
||||
async function updateDeployments() {
|
||||
if (await 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...');
|
||||
await new Promise(r => setTimeout(r, 5000));
|
||||
}
|
||||
|
||||
batch.put('v', defaultDeployments());
|
||||
}
|
||||
|
||||
async function reserializeCoins() {
|
||||
let total = 0;
|
||||
|
||||
const iter = db.iterator({
|
||||
gte: pair('c', encoding.ZERO_HASH),
|
||||
lte: pair('c', encoding.MAX_HASH),
|
||||
values: true
|
||||
});
|
||||
|
||||
while (await iter.next()) {
|
||||
const {key, value} = iter;
|
||||
const hash = key.toString('hex', 1, 33);
|
||||
const old = OldCoins.fromRaw(value, hash);
|
||||
|
||||
const coins = new Coins();
|
||||
coins.version = old.version;
|
||||
coins.hash = old.hash;
|
||||
coins.height = old.height;
|
||||
coins.coinbase = old.coinbase;
|
||||
|
||||
for (let i = 0; i < old.outputs.length; i++) {
|
||||
const coin = old.get(i);
|
||||
|
||||
if (!coin) {
|
||||
coins.outputs.push(null);
|
||||
continue;
|
||||
}
|
||||
|
||||
const output = new Output();
|
||||
output.script = coin.script;
|
||||
output.value = coin.value;
|
||||
|
||||
if (!output.script.isUnspendable())
|
||||
coins.addOutput(coin.index, output);
|
||||
}
|
||||
|
||||
coins.cleanup();
|
||||
|
||||
batch.put(key, coins.toRaw());
|
||||
|
||||
if (++total % 100000 === 0)
|
||||
console.log('Reserialized %d coins.', total);
|
||||
}
|
||||
|
||||
console.log('Reserialized %d coins.', total);
|
||||
}
|
||||
|
||||
async function reserializeUndo() {
|
||||
let total = 0;
|
||||
|
||||
const iter = db.iterator({
|
||||
gte: pair('u', encoding.ZERO_HASH),
|
||||
lte: pair('u', encoding.MAX_HASH),
|
||||
values: true
|
||||
});
|
||||
|
||||
for (;;) {
|
||||
const item = await iter.next();
|
||||
|
||||
if (!item)
|
||||
break;
|
||||
|
||||
const br = bio.read(item.value);
|
||||
const 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);
|
||||
return data.write(str, off, 'hex');
|
||||
}
|
||||
|
||||
function pair(prefix, hash) {
|
||||
const key = Buffer.allocUnsafe(33);
|
||||
if (typeof prefix === 'string')
|
||||
prefix = prefix.charCodeAt(0);
|
||||
key[0] = prefix;
|
||||
write(key, hash, 1);
|
||||
return key;
|
||||
}
|
||||
|
||||
function injectCoin(undo, coin) {
|
||||
const 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() {
|
||||
const bw = bio.write();
|
||||
let 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() {
|
||||
const bw = bio.write();
|
||||
|
||||
bw.writeU8(options.network.deploys.length);
|
||||
|
||||
for (let i = 0; i < options.network.deploys.length; i++) {
|
||||
const deployment = options.network.deploys[i];
|
||||
bw.writeU8(deployment.bit);
|
||||
bw.writeU32(deployment.startTime);
|
||||
bw.writeU32(deployment.timeout);
|
||||
}
|
||||
|
||||
return bw.render();
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await db.open();
|
||||
console.log('Opened %s.', file);
|
||||
batch = db.batch();
|
||||
await updateVersion();
|
||||
await checkTipIndex();
|
||||
await updateOptions();
|
||||
await updateDeployments();
|
||||
await reserializeCoins();
|
||||
await reserializeUndo();
|
||||
await batch.write();
|
||||
})().then(() => {
|
||||
console.log('Migration complete.');
|
||||
process.exit(0);
|
||||
});
|
||||
@ -16,7 +16,7 @@ if (process.argv.indexOf('-h') !== -1
|
||||
}
|
||||
|
||||
const assert = require('assert');
|
||||
const BDB = require('bdb');
|
||||
const bdb = require('bdb');
|
||||
const hash256 = require('bcrypto/lib/hash256');
|
||||
const BN = require('bn.js');
|
||||
const bio = require('bufio');
|
||||
@ -27,7 +27,7 @@ const CoinEntry = require('../lib/coins/coinentry');
|
||||
const UndoCoins = require('../lib/coins/undocoins');
|
||||
const Block = require('../lib/primitives/block');
|
||||
const LRU = require('../lib/utils/lru');
|
||||
const {encoding} = bio;
|
||||
const consensus = require('../lib/protocol/consensus');
|
||||
|
||||
const file = process.argv[2].replace(/\.ldb\/?$/, '');
|
||||
const shouldPrune = process.argv.indexOf('--prune') !== -1;
|
||||
@ -36,13 +36,12 @@ let hasIndex = false;
|
||||
let hasPruned = false;
|
||||
let hasSPV = false;
|
||||
|
||||
const db = new BDB({
|
||||
const db = bdb.create({
|
||||
location: file,
|
||||
db: 'leveldb',
|
||||
compression: true,
|
||||
cacheSize: 32 << 20,
|
||||
createIfMissing: false,
|
||||
bufferKeys: true
|
||||
createIfMissing: false
|
||||
});
|
||||
|
||||
// \0\0migrate
|
||||
@ -63,7 +62,7 @@ function writeJournal(batch, state, hash) {
|
||||
const data = Buffer.allocUnsafe(34);
|
||||
|
||||
if (!hash)
|
||||
hash = encoding.NULL_HASH;
|
||||
hash = consensus.NULL_HASH;
|
||||
|
||||
data[0] = MIGRATION_ID;
|
||||
data[1] = state;
|
||||
@ -76,7 +75,7 @@ async function readJournal() {
|
||||
const data = await db.get(JOURNAL_KEY);
|
||||
|
||||
if (!data)
|
||||
return [STATE_VERSION, encoding.NULL_HASH];
|
||||
return [STATE_VERSION, consensus.NULL_HASH];
|
||||
|
||||
if (data[0] !== MIGRATION_ID)
|
||||
throw new Error('Bad migration id.');
|
||||
@ -98,12 +97,12 @@ async function updateVersion() {
|
||||
|
||||
console.log('Checking version.');
|
||||
|
||||
const verRaw = await db.get('V');
|
||||
const raw = await db.get('V');
|
||||
|
||||
if (!verRaw)
|
||||
if (!raw)
|
||||
throw new Error('No DB version found!');
|
||||
|
||||
const version = verRaw.readUInt32LE(0, true);
|
||||
const version = raw.readUInt32LE(0, true);
|
||||
|
||||
if (version !== 2)
|
||||
throw Error(`DB is version ${version}.`);
|
||||
@ -121,14 +120,14 @@ async function updateVersion() {
|
||||
|
||||
await batch.write();
|
||||
|
||||
return [STATE_UNDO, encoding.NULL_HASH];
|
||||
return [STATE_UNDO, consensus.NULL_HASH];
|
||||
}
|
||||
|
||||
async function reserializeUndo(hash) {
|
||||
let tip = await getTip();
|
||||
const height = tip.height;
|
||||
|
||||
if (hash !== encoding.NULL_HASH)
|
||||
if (hash !== consensus.NULL_HASH)
|
||||
tip = await getEntry(hash);
|
||||
|
||||
console.log('Reserializing undo coins from tip %s.',
|
||||
@ -256,16 +255,16 @@ async function reserializeUndo(hash) {
|
||||
'Reserialized %d undo records (%d coins).',
|
||||
total, totalCoins);
|
||||
|
||||
return [STATE_CLEANUP, encoding.NULL_HASH];
|
||||
return [STATE_CLEANUP, consensus.NULL_HASH];
|
||||
}
|
||||
|
||||
async function cleanupIndex() {
|
||||
if (hasSPV)
|
||||
return [STATE_COINS, encoding.NULL_HASH];
|
||||
return [STATE_COINS, consensus.NULL_HASH];
|
||||
|
||||
const iter = db.iterator({
|
||||
gte: pair(0x01, encoding.ZERO_HASH),
|
||||
lte: pair(0x01, encoding.MAX_HASH),
|
||||
gte: pair(0x01, consensus.ZERO_HASH),
|
||||
lte: pair(0x01, Buffer.alloc(32, 0xff)),
|
||||
keys: true
|
||||
});
|
||||
|
||||
@ -292,23 +291,23 @@ async function cleanupIndex() {
|
||||
|
||||
console.log('Cleaned up %d undo records.', total);
|
||||
|
||||
return [STATE_COINS, encoding.NULL_HASH];
|
||||
return [STATE_COINS, consensus.NULL_HASH];
|
||||
}
|
||||
|
||||
async function reserializeCoins(hash) {
|
||||
if (hasSPV)
|
||||
return [STATE_ENTRY, encoding.NULL_HASH];
|
||||
return [STATE_ENTRY, consensus.NULL_HASH];
|
||||
|
||||
const iter = db.iterator({
|
||||
gte: pair('c', hash),
|
||||
lte: pair('c', encoding.MAX_HASH),
|
||||
lte: pair('c', Buffer.alloc(32, 0xff)),
|
||||
keys: true,
|
||||
values: true
|
||||
});
|
||||
|
||||
let start = true;
|
||||
|
||||
if (hash !== encoding.NULL_HASH) {
|
||||
if (hash !== consensus.NULL_HASH) {
|
||||
const item = await iter.next();
|
||||
if (!item)
|
||||
start = false;
|
||||
@ -369,19 +368,19 @@ async function reserializeCoins(hash) {
|
||||
|
||||
console.log('Reserialized %d coins.', total);
|
||||
|
||||
return [STATE_ENTRY, encoding.NULL_HASH];
|
||||
return [STATE_ENTRY, consensus.NULL_HASH];
|
||||
}
|
||||
|
||||
async function reserializeEntries(hash) {
|
||||
const iter = db.iterator({
|
||||
gte: pair('e', hash),
|
||||
lte: pair('e', encoding.MAX_HASH),
|
||||
lte: pair('e', Buffer.alloc(32, 0xff)),
|
||||
values: true
|
||||
});
|
||||
|
||||
let start = true;
|
||||
|
||||
if (hash !== encoding.NULL_HASH) {
|
||||
if (hash !== consensus.NULL_HASH) {
|
||||
const item = await iter.next();
|
||||
if (!item)
|
||||
start = false;
|
||||
@ -420,7 +419,7 @@ async function reserializeEntries(hash) {
|
||||
|
||||
console.log('Reserialized %d entries.', total);
|
||||
|
||||
return [STATE_FINAL, encoding.NULL_HASH];
|
||||
return [STATE_FINAL, consensus.NULL_HASH];
|
||||
}
|
||||
|
||||
async function finalize() {
|
||||
@ -433,7 +432,7 @@ async function finalize() {
|
||||
batch.put('V', data);
|
||||
|
||||
// This has bugged me for a while.
|
||||
batch.del(pair('n', encoding.ZERO_HASH));
|
||||
batch.del(pair('n', consensus.ZERO_HASH));
|
||||
|
||||
if (shouldPrune) {
|
||||
const data = await db.get('O');
|
||||
@ -456,7 +455,7 @@ async function finalize() {
|
||||
|
||||
await db.compactRange();
|
||||
|
||||
return [STATE_DONE, encoding.NULL_HASH];
|
||||
return [STATE_DONE, consensus.NULL_HASH];
|
||||
}
|
||||
|
||||
async function getMeta(coin, prevout) {
|
||||
@ -588,7 +587,7 @@ function entryFromRaw(data) {
|
||||
entry.version = br.readU32();
|
||||
entry.prevBlock = br.readHash('hex');
|
||||
entry.merkleRoot = br.readHash('hex');
|
||||
entry.ts = br.readU32();
|
||||
entry.time = br.readU32();
|
||||
entry.bits = br.readU32();
|
||||
entry.nonce = br.readU32();
|
||||
entry.height = br.readU32();
|
||||
@ -603,7 +602,7 @@ function entryToRaw(entry, main) {
|
||||
bw.writeU32(entry.version);
|
||||
bw.writeHash(entry.prevBlock);
|
||||
bw.writeHash(entry.merkleRoot);
|
||||
bw.writeU32(entry.ts);
|
||||
bw.writeU32(entry.time);
|
||||
bw.writeU32(entry.bits);
|
||||
bw.writeU32(entry.nonce);
|
||||
bw.writeU32(entry.height);
|
||||
@ -684,7 +683,7 @@ reserializeEntries;
|
||||
// [state, hash] = await reserializeEntries(hash);
|
||||
|
||||
if (state === STATE_ENTRY)
|
||||
[state, hash] = [STATE_FINAL, encoding.NULL_HASH];
|
||||
[state, hash] = [STATE_FINAL, consensus.NULL_HASH];
|
||||
|
||||
if (state === STATE_FINAL)
|
||||
[state, hash] = await finalize();
|
||||
|
||||
@ -1,611 +0,0 @@
|
||||
/*!
|
||||
* coins.js - coins object for bcoin
|
||||
* Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
|
||||
* https://github.com/bcoin-org/bcoin
|
||||
*/
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const bio = require('bufio');
|
||||
const util = require('../lib/utils/util');
|
||||
const Coin = require('../lib/primitives/coin');
|
||||
const Output = require('../lib/primitives/output');
|
||||
const {compress, decompress} = require('./compress-old');
|
||||
const {encoding} = bio;
|
||||
|
||||
/**
|
||||
* Represents the outputs for a single transaction.
|
||||
* @exports Coins
|
||||
* @constructor
|
||||
* @param {TX|Object} tx/options - TX or options object.
|
||||
* @property {Hash} hash - Transaction hash.
|
||||
* @property {Number} version - Transaction version.
|
||||
* @property {Number} height - Transaction height (-1 if unconfirmed).
|
||||
* @property {Boolean} coinbase - Whether the containing
|
||||
* transaction is a coinbase.
|
||||
* @property {Coin[]} outputs - Coins.
|
||||
*/
|
||||
|
||||
function Coins(options) {
|
||||
if (!(this instanceof Coins))
|
||||
return new Coins(options);
|
||||
|
||||
this.version = 1;
|
||||
this.hash = encoding.NULL_HASH;
|
||||
this.height = -1;
|
||||
this.coinbase = true;
|
||||
this.outputs = [];
|
||||
|
||||
if (options)
|
||||
this.fromOptions(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject properties from options object.
|
||||
* @private
|
||||
* @param {Object} options
|
||||
*/
|
||||
|
||||
Coins.prototype.fromOptions = function fromOptions(options) {
|
||||
if (options.version != null) {
|
||||
assert((options.version >>> 0) === options.version);
|
||||
this.version = options.version;
|
||||
}
|
||||
|
||||
if (options.hash) {
|
||||
assert(typeof options.hash === 'string');
|
||||
this.hash = options.hash;
|
||||
}
|
||||
|
||||
if (options.height != null) {
|
||||
assert(Number.isSafeInteger(options.height));
|
||||
this.height = options.height;
|
||||
}
|
||||
|
||||
if (options.coinbase != null) {
|
||||
assert(typeof options.coinbase === 'boolean');
|
||||
this.coinbase = options.coinbase;
|
||||
}
|
||||
|
||||
if (options.outputs) {
|
||||
assert(Array.isArray(options.outputs));
|
||||
this.outputs = options.outputs;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate coins from options object.
|
||||
* @param {Object} options
|
||||
* @returns {Coins}
|
||||
*/
|
||||
|
||||
Coins.fromOptions = function fromOptions(options) {
|
||||
return new Coins().fromOptions(options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a single coin to the collection.
|
||||
* @param {Coin} coin
|
||||
*/
|
||||
|
||||
Coins.prototype.add = function add(coin) {
|
||||
if (this.outputs.length === 0) {
|
||||
this.version = coin.version;
|
||||
this.hash = coin.hash;
|
||||
this.height = coin.height;
|
||||
this.coinbase = coin.coinbase;
|
||||
}
|
||||
|
||||
while (this.outputs.length <= coin.index)
|
||||
this.outputs.push(null);
|
||||
|
||||
if (coin.script.isUnspendable()) {
|
||||
this.outputs[coin.index] = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.outputs[coin.index] = CoinEntry.fromCoin(coin);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the collection has a coin.
|
||||
* @param {Number} index
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Coins.prototype.has = function has(index) {
|
||||
if (index >= this.outputs.length)
|
||||
return false;
|
||||
|
||||
return this.outputs[index] != null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a coin.
|
||||
* @param {Number} index
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
Coins.prototype.get = function get(index) {
|
||||
if (index >= this.outputs.length)
|
||||
return;
|
||||
|
||||
const coin = this.outputs[index];
|
||||
|
||||
if (!coin)
|
||||
return;
|
||||
|
||||
return coin.toCoin(this, index);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a coin and return it.
|
||||
* @param {Number} index
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
Coins.prototype.spend = function spend(index) {
|
||||
const coin = this.get(index);
|
||||
|
||||
if (!coin)
|
||||
return;
|
||||
|
||||
this.outputs[index] = null;
|
||||
|
||||
return coin;
|
||||
};
|
||||
|
||||
/**
|
||||
* Count up to the last available index.
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Coins.prototype.size = function size() {
|
||||
let index = -1;
|
||||
|
||||
for (let i = this.outputs.length - 1; i >= 0; i--) {
|
||||
const output = this.outputs[i];
|
||||
if (output) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return index + 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the coins are fully spent.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Coins.prototype.isEmpty = function isEmpty() {
|
||||
return this.size() === 0;
|
||||
};
|
||||
|
||||
/*
|
||||
* Coins serialization:
|
||||
* version: varint
|
||||
* bits: uint32 (31-bit height | 1-bit coinbase-flag)
|
||||
* spent-field: varint size | bitfield (0=unspent, 1=spent)
|
||||
* outputs (repeated):
|
||||
* compressed-script:
|
||||
* prefix: 0x00 = varint size | raw script
|
||||
* 0x01 = 20 byte pubkey hash
|
||||
* 0x02 = 20 byte script hash
|
||||
* 0x03 = 33 byte compressed key
|
||||
* data: script data, dictated by the prefix
|
||||
* value: varint
|
||||
*
|
||||
* The compression below sacrifices some cpu in exchange
|
||||
* for reduced size, but in some cases the use of varints
|
||||
* actually increases speed (varint versions and values
|
||||
* for example). We do as much compression as possible
|
||||
* without sacrificing too much cpu. Value compression
|
||||
* is intentionally excluded for now as it seems to be
|
||||
* too much of a perf hit. Maybe when v8 optimizes
|
||||
* non-smi arithmetic better we can enable it.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Serialize the coins object.
|
||||
* @param {TX|Coins} tx
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
Coins.prototype.toRaw = function toRaw() {
|
||||
const bw = bio.write();
|
||||
const length = this.size();
|
||||
const len = Math.ceil(length / 8);
|
||||
|
||||
// Return nothing if we're fully spent.
|
||||
if (length === 0)
|
||||
return;
|
||||
|
||||
// Varint version: hopefully we
|
||||
// never run into `-1` versions.
|
||||
bw.writeVarint(this.version);
|
||||
|
||||
// Create the `bits` value:
|
||||
// (height | coinbase-flag).
|
||||
let bits = this.height << 1;
|
||||
|
||||
// Append the coinbase bit.
|
||||
if (this.coinbase)
|
||||
bits |= 1;
|
||||
|
||||
if (bits < 0)
|
||||
bits += 0x100000000;
|
||||
|
||||
// Making this a varint would actually
|
||||
// make 99% of coins bigger. Varints
|
||||
// are really only useful up until
|
||||
// 0x10000, but since we're also
|
||||
// storing the coinbase flag on the
|
||||
// lo bit, varints are useless (and
|
||||
// actually harmful) after height
|
||||
// 32767 (0x7fff).
|
||||
bw.writeU32(bits);
|
||||
|
||||
// Fill the spent field with zeroes to avoid
|
||||
// allocating a buffer. We mark the spents
|
||||
// after rendering the final buffer.
|
||||
bw.writeVarint(len);
|
||||
const start = bw.offset;
|
||||
bw.fill(0, len);
|
||||
|
||||
// Write the compressed outputs.
|
||||
for (let i = 0; i < length; i++) {
|
||||
const output = this.outputs[i];
|
||||
|
||||
if (!output)
|
||||
continue;
|
||||
|
||||
output.toWriter(bw);
|
||||
}
|
||||
|
||||
// Render the buffer with all
|
||||
// zeroes in the spent field.
|
||||
const data = bw.render();
|
||||
|
||||
// Mark the spents in the spent field.
|
||||
// This is essentially a NOP for new coins.
|
||||
for (let i = 0; i < length; i++) {
|
||||
const output = this.outputs[i];
|
||||
|
||||
if (output)
|
||||
continue;
|
||||
|
||||
const bit = i % 8;
|
||||
let oct = (i - bit) / 8;
|
||||
oct += start;
|
||||
|
||||
data[oct] |= 1 << (7 - bit);
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse serialized coins.
|
||||
* @param {Buffer} data
|
||||
* @param {Hash} hash
|
||||
* @returns {Object} A "naked" coins object.
|
||||
*/
|
||||
|
||||
Coins.prototype.fromRaw = function fromRaw(data, hash, index) {
|
||||
const br = bio.read(data);
|
||||
let pos = 0;
|
||||
|
||||
this.version = br.readVarint();
|
||||
|
||||
const bits = br.readU32();
|
||||
|
||||
this.height = bits >>> 1;
|
||||
this.hash = hash;
|
||||
this.coinbase = (bits & 1) !== 0;
|
||||
|
||||
// Mark the start of the spent field and
|
||||
// seek past it to avoid reading a buffer.
|
||||
const len = br.readVarint();
|
||||
const start = br.offset;
|
||||
br.seek(len);
|
||||
|
||||
while (br.left()) {
|
||||
const bit = pos % 8;
|
||||
let oct = (pos - bit) / 8;
|
||||
oct += start;
|
||||
|
||||
// Read a single bit out of the spent field.
|
||||
let spent = data[oct] >>> (7 - bit);
|
||||
spent &= 1;
|
||||
|
||||
// Already spent.
|
||||
if (spent) {
|
||||
this.outputs.push(null);
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Store the offset and size
|
||||
// in the compressed coin object.
|
||||
const coin = CoinEntry.fromReader(br);
|
||||
|
||||
this.outputs.push(coin);
|
||||
pos++;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse a single serialized coin.
|
||||
* @param {Buffer} data
|
||||
* @param {Hash} hash
|
||||
* @param {Number} index
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
Coins.parseCoin = function parseCoin(data, hash, index) {
|
||||
const br = bio.read(data);
|
||||
const coin = new Coin();
|
||||
let pos = 0;
|
||||
|
||||
coin.version = br.readVarint();
|
||||
|
||||
const bits = br.readU32();
|
||||
|
||||
coin.hash = hash;
|
||||
coin.index = index;
|
||||
coin.height = bits >>> 1;
|
||||
coin.hash = hash;
|
||||
coin.coinbase = (bits & 1) !== 0;
|
||||
|
||||
// Mark the start of the spent field and
|
||||
// seek past it to avoid reading a buffer.
|
||||
const len = br.readVarint();
|
||||
const start = br.offset;
|
||||
br.seek(len);
|
||||
|
||||
while (br.left()) {
|
||||
const bit = pos % 8;
|
||||
let oct = (pos - bit) / 8;
|
||||
oct += start;
|
||||
|
||||
// Read a single bit out of the spent field.
|
||||
let spent = data[oct] >>> (7 - bit);
|
||||
spent &= 1;
|
||||
|
||||
// We found our coin.
|
||||
if (pos === index) {
|
||||
if (spent)
|
||||
return;
|
||||
decompress.script(coin.script, br);
|
||||
coin.value = br.readVarint();
|
||||
return coin;
|
||||
}
|
||||
|
||||
// Already spent.
|
||||
if (spent) {
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip past the compressed coin.
|
||||
skipCoin(br);
|
||||
pos++;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate coins from a serialized Buffer.
|
||||
* @param {Buffer} data
|
||||
* @param {Hash} hash - Transaction hash.
|
||||
* @returns {Coins}
|
||||
*/
|
||||
|
||||
Coins.fromRaw = function fromRaw(data, hash) {
|
||||
return new Coins().fromRaw(data, hash);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from tx.
|
||||
* @private
|
||||
* @param {TX} tx
|
||||
*/
|
||||
|
||||
Coins.prototype.fromTX = function fromTX(tx) {
|
||||
this.version = tx.version;
|
||||
this.hash = tx.hash('hex');
|
||||
this.height = tx.height;
|
||||
this.coinbase = tx.isCoinbase();
|
||||
|
||||
for (let i = 0; i < tx.outputs.length; i++) {
|
||||
const output = tx.outputs[i];
|
||||
|
||||
if (output.script.isUnspendable()) {
|
||||
this.outputs.push(null);
|
||||
continue;
|
||||
}
|
||||
|
||||
this.outputs.push(CoinEntry.fromTX(tx, i));
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate a coins object from a transaction.
|
||||
* @param {TX} tx
|
||||
* @returns {Coins}
|
||||
*/
|
||||
|
||||
Coins.fromTX = function fromTX(tx) {
|
||||
return new Coins().fromTX(tx);
|
||||
};
|
||||
|
||||
/**
|
||||
* A compressed coin is an object which defers
|
||||
* parsing of a coin. Say there is a transaction
|
||||
* with 100 outputs. When a block comes in,
|
||||
* there may only be _one_ input in that entire
|
||||
* block which redeems an output from that
|
||||
* transaction. When parsing the Coins, there
|
||||
* is no sense to get _all_ of them into their
|
||||
* abstract form. A compressed coin is just a
|
||||
* pointer to that coin in the Coins buffer, as
|
||||
* well as a size. Parsing is done only if that
|
||||
* coin is being redeemed.
|
||||
* @constructor
|
||||
* @private
|
||||
* @param {Number} offset
|
||||
* @param {Number} size
|
||||
* @param {Buffer} raw
|
||||
*/
|
||||
|
||||
function CoinEntry() {
|
||||
this.offset = 0;
|
||||
this.size = 0;
|
||||
this.raw = null;
|
||||
this.output = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the deferred data and return a Coin.
|
||||
* @param {Coins} coins
|
||||
* @param {Number} index
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
CoinEntry.prototype.toCoin = function toCoin(coins, index) {
|
||||
const coin = new Coin();
|
||||
|
||||
// Load in all necessary properties
|
||||
// from the parent Coins object.
|
||||
coin.version = coins.version;
|
||||
coin.coinbase = coins.coinbase;
|
||||
coin.height = coins.height;
|
||||
coin.hash = coins.hash;
|
||||
coin.index = index;
|
||||
|
||||
if (this.output) {
|
||||
coin.script = this.output.script;
|
||||
coin.value = this.output.value;
|
||||
return coin;
|
||||
}
|
||||
|
||||
const br = bio.read(this.raw);
|
||||
|
||||
// Seek to the coin's offset.
|
||||
br.seek(this.offset);
|
||||
|
||||
decompress.script(coin.script, br);
|
||||
|
||||
coin.value = br.readVarint();
|
||||
|
||||
return coin;
|
||||
};
|
||||
|
||||
/**
|
||||
* Slice off the part of the buffer
|
||||
* relevant to this particular coin.
|
||||
*/
|
||||
|
||||
CoinEntry.prototype.toWriter = function toWriter(bw) {
|
||||
if (this.output) {
|
||||
compress.script(this.output.script, bw);
|
||||
bw.writeVarint(this.output.value);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(this.raw);
|
||||
|
||||
// If we read this coin from the db and
|
||||
// didn't use it, it's still in its
|
||||
// compressed form. Just write it back
|
||||
// as a buffer for speed.
|
||||
const raw = this.raw.slice(this.offset, this.offset + this.size);
|
||||
|
||||
bw.writeBytes(raw);
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate compressed coin from reader.
|
||||
* @param {BufferReader} br
|
||||
* @returns {CoinEntry}
|
||||
*/
|
||||
|
||||
CoinEntry.fromReader = function fromReader(br) {
|
||||
const entry = new CoinEntry();
|
||||
entry.offset = br.offset;
|
||||
entry.size = skipCoin(br);
|
||||
entry.raw = br.data;
|
||||
return entry;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate compressed coin from tx.
|
||||
* @param {TX} tx
|
||||
* @param {Number} index
|
||||
* @returns {CoinEntry}
|
||||
*/
|
||||
|
||||
CoinEntry.fromTX = function fromTX(tx, index) {
|
||||
const entry = new CoinEntry();
|
||||
entry.output = tx.outputs[index];
|
||||
return entry;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate compressed coin from coin.
|
||||
* @param {Coin} coin
|
||||
* @returns {CoinEntry}
|
||||
*/
|
||||
|
||||
CoinEntry.fromCoin = function fromCoin(coin) {
|
||||
const entry = new CoinEntry();
|
||||
entry.output = new Output();
|
||||
entry.output.script = coin.script;
|
||||
entry.output.value = coin.value;
|
||||
return entry;
|
||||
};
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
function skipCoin(br) {
|
||||
const start = br.offset;
|
||||
|
||||
// Skip past the compressed scripts.
|
||||
switch (br.readU8()) {
|
||||
case 0:
|
||||
br.seek(br.readVarint());
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
br.seek(20);
|
||||
break;
|
||||
case 3:
|
||||
br.seek(33);
|
||||
break;
|
||||
default:
|
||||
throw new Error('Bad prefix.');
|
||||
}
|
||||
|
||||
// Skip past the value.
|
||||
br.readVarint();
|
||||
|
||||
return br.offset - start;
|
||||
}
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
module.exports = Coins;
|
||||
@ -1,149 +0,0 @@
|
||||
/*!
|
||||
* coinview.js - coinview object for bcoin
|
||||
* Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
|
||||
* https://github.com/bcoin-org/bcoin
|
||||
*/
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const Coins = require('./coins-old');
|
||||
|
||||
/**
|
||||
* A collections of {@link Coins} objects.
|
||||
* @exports CoinView
|
||||
* @constructor
|
||||
* @param {Object} coins - A hash-to-coins map.
|
||||
* @property {Object} coins
|
||||
*/
|
||||
|
||||
function CoinView(coins) {
|
||||
if (!(this instanceof CoinView))
|
||||
return new CoinView(coins);
|
||||
|
||||
this.coins = coins || {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Add coins to the collection.
|
||||
* @param {Coins} coins
|
||||
*/
|
||||
|
||||
CoinView.prototype.add = function add(coins) {
|
||||
this.coins[coins.hash] = coins;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a coin to the collection.
|
||||
* @param {Coin} coin
|
||||
*/
|
||||
|
||||
CoinView.prototype.addCoin = function addCoin(coin) {
|
||||
assert(typeof coin.hash === 'string');
|
||||
if (!this.coins[coin.hash])
|
||||
this.coins[coin.hash] = new Coins();
|
||||
this.coins[coin.hash].add(coin);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a tx to the collection.
|
||||
* @param {TX} tx
|
||||
*/
|
||||
|
||||
CoinView.prototype.addTX = function addTX(tx) {
|
||||
this.add(Coins.fromTX(tx));
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a coin.
|
||||
* @param {Hash} hash
|
||||
* @param {Number} index
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
CoinView.prototype.get = function get(hash, index) {
|
||||
const coins = this.coins[hash];
|
||||
|
||||
if (!coins)
|
||||
return;
|
||||
|
||||
return coins.get(index);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the collection has a coin.
|
||||
* @param {Hash} hash
|
||||
* @param {Number} index
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
CoinView.prototype.has = function has(hash, index) {
|
||||
const coins = this.coins[hash];
|
||||
|
||||
if (!coins)
|
||||
return false;
|
||||
|
||||
return coins.has(index);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a coin and return it.
|
||||
* @param {Hash} hash
|
||||
* @param {Number} index
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
CoinView.prototype.spend = function spend(hash, index) {
|
||||
const coins = this.coins[hash];
|
||||
|
||||
if (!coins)
|
||||
return;
|
||||
|
||||
return coins.spend(index);
|
||||
};
|
||||
|
||||
/**
|
||||
* Fill transaction(s) with coins.
|
||||
* @param {TX} tx
|
||||
* @returns {Boolean} True if all inputs were filled.
|
||||
*/
|
||||
|
||||
CoinView.prototype.fillCoins = function fillCoins(tx) {
|
||||
let i, input, prevout;
|
||||
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
input = tx.inputs[i];
|
||||
prevout = input.prevout;
|
||||
input.coin = this.spend(prevout.hash, prevout.index);
|
||||
if (!input.coin)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert collection to an array.
|
||||
* @returns {Coins[]}
|
||||
*/
|
||||
|
||||
CoinView.prototype.toArray = function toArray() {
|
||||
const keys = Object.keys(this.coins);
|
||||
const out = [];
|
||||
let i, hash;
|
||||
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
hash = keys[i];
|
||||
out.push(this.coins[hash]);
|
||||
}
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
module.exports = CoinView;
|
||||
@ -1,247 +0,0 @@
|
||||
/*!
|
||||
* compress.js - coin compressor for bcoin
|
||||
* Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
|
||||
* https://github.com/bcoin-org/bcoin
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const secp256k1 = require('bcrypto/lib/secp256k1');
|
||||
|
||||
/*
|
||||
* Compression
|
||||
*/
|
||||
|
||||
/**
|
||||
* Compress a script, write directly to the buffer.
|
||||
* @param {Script} script
|
||||
* @param {BufferWriter} bw
|
||||
*/
|
||||
|
||||
function compressScript(script, bw) {
|
||||
// Attempt to compress the output scripts.
|
||||
// We can _only_ ever compress them if
|
||||
// they are serialized as minimaldata, as
|
||||
// we need to recreate them when we read
|
||||
// them.
|
||||
|
||||
// P2PKH -> 1 | key-hash
|
||||
// Saves 5 bytes.
|
||||
if (script.isPubkeyhash(true)) {
|
||||
const data = script.code[2].data;
|
||||
bw.writeU8(1);
|
||||
bw.writeBytes(data);
|
||||
return bw;
|
||||
}
|
||||
|
||||
// P2SH -> 2 | script-hash
|
||||
// Saves 3 bytes.
|
||||
if (script.isScripthash()) {
|
||||
const data = script.code[1].data;
|
||||
bw.writeU8(2);
|
||||
bw.writeBytes(data);
|
||||
return bw;
|
||||
}
|
||||
|
||||
// P2PK -> 3 | compressed-key
|
||||
// Only works if the key is valid.
|
||||
// Saves up to 34 bytes.
|
||||
if (script.isPubkey(true)) {
|
||||
let data = script.code[0].data;
|
||||
if (secp256k1.publicKeyVerify(data)) {
|
||||
data = compressKey(data);
|
||||
bw.writeU8(3);
|
||||
bw.writeBytes(data);
|
||||
return bw;
|
||||
}
|
||||
}
|
||||
|
||||
// Raw -> 0 | varlen | script
|
||||
bw.writeU8(0);
|
||||
bw.writeVarBytes(script.toRaw());
|
||||
|
||||
return bw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompress a script from buffer reader.
|
||||
* @param {Script} script
|
||||
* @param {BufferReader} br
|
||||
*/
|
||||
|
||||
function decompressScript(script, br) {
|
||||
let data;
|
||||
|
||||
// Decompress the script.
|
||||
switch (br.readU8()) {
|
||||
case 0:
|
||||
data = br.readVarBytes();
|
||||
script.fromRaw(data);
|
||||
break;
|
||||
case 1:
|
||||
data = br.readBytes(20, true);
|
||||
script.fromPubkeyhash(data);
|
||||
break;
|
||||
case 2:
|
||||
data = br.readBytes(20, true);
|
||||
script.fromScripthash(data);
|
||||
break;
|
||||
case 3:
|
||||
data = br.readBytes(33, true);
|
||||
// Decompress the key. If this fails,
|
||||
// we have database corruption!
|
||||
data = decompressKey(data);
|
||||
script.fromPubkey(data);
|
||||
break;
|
||||
default:
|
||||
throw new Error('Bad prefix.');
|
||||
}
|
||||
|
||||
return script;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compress value using an exponent. Takes advantage of
|
||||
* the fact that many bitcoin values are divisible by 10.
|
||||
* @see https://github.com/btcsuite/btcd/blob/master/blockchain/compress.go
|
||||
* @param {Amount} value
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
function compressValue(value) {
|
||||
if (value === 0)
|
||||
return 0;
|
||||
|
||||
let exp = 0;
|
||||
while (value % 10 === 0 && exp < 9) {
|
||||
value /= 10;
|
||||
exp++;
|
||||
}
|
||||
|
||||
if (exp < 9) {
|
||||
const last = value % 10;
|
||||
value = (value - last) / 10;
|
||||
return 1 + 10 * (9 * value + last - 1) + exp;
|
||||
}
|
||||
|
||||
return 10 + 10 * (value - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompress value.
|
||||
* @param {Number} value - Compressed value.
|
||||
* @returns {Amount} value
|
||||
*/
|
||||
|
||||
function decompressValue(value) {
|
||||
if (value === 0)
|
||||
return 0;
|
||||
|
||||
value--;
|
||||
|
||||
let exp = value % 10;
|
||||
value = (value - exp) / 10;
|
||||
|
||||
let n;
|
||||
if (exp < 9) {
|
||||
const last = value % 9;
|
||||
value = (value - last) / 9;
|
||||
n = value * 10 + last + 1;
|
||||
} else {
|
||||
n = value + 1;
|
||||
}
|
||||
|
||||
while (exp > 0) {
|
||||
n *= 10;
|
||||
exp--;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compress a public key to coins compression format.
|
||||
* @param {Buffer} key
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
function compressKey(key) {
|
||||
let out;
|
||||
|
||||
switch (key[0]) {
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
// Key is already compressed.
|
||||
out = key;
|
||||
break;
|
||||
case 0x04:
|
||||
case 0x06:
|
||||
case 0x07:
|
||||
// Compress the key normally.
|
||||
out = secp256k1.publicKeyConvert(key, true);
|
||||
// Store the original format (which
|
||||
// may be a hybrid byte) in the hi
|
||||
// 3 bits so we can restore it later.
|
||||
// The hi bits being set also lets us
|
||||
// know that this key was originally
|
||||
// decompressed.
|
||||
out[0] |= key[0] << 2;
|
||||
break;
|
||||
default:
|
||||
throw new Error('Bad point format.');
|
||||
}
|
||||
|
||||
assert(out.length === 33);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompress a public key from the coins compression format.
|
||||
* @param {Buffer} key
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
function decompressKey(key) {
|
||||
const format = key[0] >>> 2;
|
||||
|
||||
assert(key.length === 33);
|
||||
|
||||
// Hi bits are not set. This key
|
||||
// is not meant to be decompressed.
|
||||
if (format === 0)
|
||||
return key;
|
||||
|
||||
// Decompress the key, and off the
|
||||
// low bits so publicKeyConvert
|
||||
// actually understands it.
|
||||
key[0] &= 0x03;
|
||||
const out = secp256k1.publicKeyConvert(key, false);
|
||||
|
||||
// Reset the hi bits so as not to
|
||||
// mutate the original buffer.
|
||||
key[0] |= format << 2;
|
||||
|
||||
// Set the original format, which
|
||||
// may have been a hybrid prefix byte.
|
||||
out[0] = format;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
exports.compress = {
|
||||
script: compressScript,
|
||||
value: compressValue,
|
||||
key: compressKey
|
||||
};
|
||||
|
||||
exports.decompress = {
|
||||
script: decompressScript,
|
||||
value: decompressValue,
|
||||
key: decompressKey
|
||||
};
|
||||
@ -1,148 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const BDB = require('bdb');
|
||||
const bio = require('bufio');
|
||||
const hash256 = require('bcrypto/lib/hash256');
|
||||
const BN = require('bn.js');
|
||||
const util = require('../lib/utils/util');
|
||||
const {encoding} = bio;
|
||||
|
||||
const DUMMY = Buffer.from([0]);
|
||||
|
||||
let file = process.argv[2];
|
||||
let batch;
|
||||
|
||||
assert(typeof file === 'string', 'Please pass in a database path.');
|
||||
|
||||
file = file.replace(/\.ldb\/?$/, '');
|
||||
|
||||
const db = new BDB({
|
||||
location: file,
|
||||
db: 'leveldb',
|
||||
compression: true,
|
||||
cacheSize: 32 << 20,
|
||||
createIfMissing: false,
|
||||
bufferKeys: true
|
||||
});
|
||||
|
||||
async function checkVersion() {
|
||||
console.log('Checking version.');
|
||||
|
||||
const data = await db.get('V');
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
const ver = data.readUInt32LE(0, true);
|
||||
|
||||
if (ver !== 1)
|
||||
throw Error(`DB is version ${ver}.`);
|
||||
}
|
||||
|
||||
function entryFromRaw(data) {
|
||||
const p = bio.read(data, true);
|
||||
const hash = hash256.digest(p.readBytes(80));
|
||||
const entry = {};
|
||||
|
||||
p.seek(-80);
|
||||
|
||||
entry.hash = hash.toString('hex');
|
||||
entry.version = p.readU32(); // Technically signed
|
||||
entry.prevBlock = p.readHash('hex');
|
||||
entry.merkleRoot = p.readHash('hex');
|
||||
entry.time = p.readU32();
|
||||
entry.bits = p.readU32();
|
||||
entry.nonce = p.readU32();
|
||||
entry.height = p.readU32();
|
||||
entry.chainwork = new BN(p.readBytes(32), 'le');
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
function getEntries() {
|
||||
return db.values({
|
||||
gte: pair('e', encoding.ZERO_HASH),
|
||||
lte: pair('e', encoding.MAX_HASH),
|
||||
parse: entryFromRaw
|
||||
});
|
||||
}
|
||||
|
||||
async function getTip(entry) {
|
||||
const state = await db.get('R');
|
||||
assert(state);
|
||||
const tip = state.toString('hex', 0, 32);
|
||||
const data = await db.get(pair('e', tip));
|
||||
assert(data);
|
||||
return entryFromRaw(data);
|
||||
}
|
||||
|
||||
async function isMainChain(entry, tip) {
|
||||
if (entry.hash === tip)
|
||||
return true;
|
||||
|
||||
if (await db.get(pair('n', entry.hash)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// And this insane function is why we should
|
||||
// be indexing tips in the first place!
|
||||
async function indexTips() {
|
||||
const entries = await getEntries();
|
||||
const tip = await getTip();
|
||||
const tips = [];
|
||||
const orphans = [];
|
||||
const prevs = {};
|
||||
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
const entry = entries[i];
|
||||
const main = await isMainChain(entry, tip.hash);
|
||||
if (!main) {
|
||||
orphans.push(entry);
|
||||
prevs[entry.prevBlock] = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < orphans.length; i++) {
|
||||
const orphan = orphans[i];
|
||||
if (!prevs[orphan.hash])
|
||||
tips.push(orphan.hash);
|
||||
}
|
||||
|
||||
tips.push(tip.hash);
|
||||
|
||||
for (let i = 0; i < tips.length; i++) {
|
||||
const tip = tips[i];
|
||||
console.log('Indexing chain tip: %s.', util.revHex(tip));
|
||||
batch.put(pair('p', tip), DUMMY);
|
||||
}
|
||||
}
|
||||
|
||||
function write(data, str, off) {
|
||||
if (Buffer.isBuffer(str))
|
||||
return str.copy(data, off);
|
||||
return data.write(str, off, 'hex');
|
||||
}
|
||||
|
||||
function pair(prefix, hash) {
|
||||
const key = Buffer.allocUnsafe(33);
|
||||
if (typeof prefix === 'string')
|
||||
prefix = prefix.charCodeAt(0);
|
||||
key[0] = prefix;
|
||||
write(key, hash, 1);
|
||||
return key;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await db.open();
|
||||
console.log('Opened %s.', file);
|
||||
batch = db.batch();
|
||||
await checkVersion();
|
||||
await indexTips();
|
||||
await batch.write();
|
||||
})().then(() => {
|
||||
console.log('Migration complete.');
|
||||
process.exit(0);
|
||||
});
|
||||
@ -1,364 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const bcoin = require('../');
|
||||
const bio = require('bufio');
|
||||
const walletdb = require('../lib/wallet/walletdb');
|
||||
const Path = require('../lib/wallet/path');
|
||||
const MasterKey = require('../lib/wallet/masterkey');
|
||||
const Account = require('../lib/wallet/account');
|
||||
const Wallet = require('../lib/wallet/wallet');
|
||||
const KeyRing = require('../lib/primitives/keyring');
|
||||
const layout = walletdb.layout;
|
||||
const {encoding} = bio;
|
||||
|
||||
let file = process.argv[2];
|
||||
let batch;
|
||||
|
||||
assert(typeof file === 'string', 'Please pass in a database path.');
|
||||
|
||||
file = file.replace(/\.ldb\/?$/, '');
|
||||
|
||||
const db = bcoin.ldb({
|
||||
location: file,
|
||||
db: 'leveldb',
|
||||
compression: true,
|
||||
cacheSize: 32 << 20,
|
||||
createIfMissing: false,
|
||||
bufferKeys: true
|
||||
});
|
||||
|
||||
async function updateVersion() {
|
||||
const bak = `${process.env.HOME}/walletdb-bak-${Date.now()}.ldb`;
|
||||
|
||||
console.log('Checking version.');
|
||||
|
||||
const data = await db.get('V');
|
||||
assert(data, 'No version.');
|
||||
|
||||
let ver = data.readUInt32LE(0, true);
|
||||
|
||||
if (ver !== 2)
|
||||
throw Error(`DB is version ${ver}.`);
|
||||
|
||||
console.log('Backing up DB to: %s.', bak);
|
||||
|
||||
await db.backup(bak);
|
||||
|
||||
ver = Buffer.allocUnsafe(4);
|
||||
ver.writeUInt32LE(3, 0, true);
|
||||
batch.put('V', ver);
|
||||
}
|
||||
|
||||
async function updatePathMap() {
|
||||
let total = 0;
|
||||
|
||||
const iter = db.iterator({
|
||||
gte: layout.p(encoding.NULL_HASH),
|
||||
lte: layout.p(encoding.HIGH_HASH),
|
||||
values: true
|
||||
});
|
||||
|
||||
console.log('Migrating path map.');
|
||||
|
||||
while (await iter.next()) {
|
||||
const {key, value} = iter;
|
||||
|
||||
total++;
|
||||
|
||||
const hash = layout.pp(key);
|
||||
const oldPaths = parsePaths(value, hash);
|
||||
const keys = Object.keys(oldPaths);
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
keys[i] = Number(keys[i]);
|
||||
const key = keys[i];
|
||||
const oldPath = oldPaths[key];
|
||||
const path = new Path(oldPath);
|
||||
if (path.data) {
|
||||
if (path.encrypted) {
|
||||
console.log(
|
||||
'Cannot migrate encrypted import: %s (%s)',
|
||||
path.data.toString('hex'),
|
||||
path.toAddress().toBase58());
|
||||
continue;
|
||||
}
|
||||
const ring = keyFromRaw(path.data);
|
||||
path.data = new KeyRing(ring).toRaw();
|
||||
}
|
||||
batch.put(layout.P(key, hash), path.toRaw());
|
||||
}
|
||||
|
||||
batch.put(key, serializeWallets(keys.sort()));
|
||||
}
|
||||
|
||||
console.log('Migrated %d paths.', total);
|
||||
}
|
||||
|
||||
async function updateAccounts() {
|
||||
let total = 0;
|
||||
|
||||
const iter = db.iterator({
|
||||
gte: layout.a(0, 0),
|
||||
lte: layout.a(0xffffffff, 0xffffffff),
|
||||
values: true
|
||||
});
|
||||
|
||||
console.log('Migrating accounts.');
|
||||
|
||||
for (;;) {
|
||||
const item = await iter.next();
|
||||
|
||||
if (!item)
|
||||
break;
|
||||
|
||||
total++;
|
||||
let account = accountFromRaw(item.value, item.key);
|
||||
account = new Account({ network: account.network, options: {} }, account);
|
||||
batch.put(item.key, account.toRaw());
|
||||
|
||||
if (account._old) {
|
||||
batch.del(layout.i(account.wid, account._old));
|
||||
const buf = Buffer.allocUnsafe(4);
|
||||
buf.writeUInt32LE(account.accountIndex, 0, true);
|
||||
batch.put(layout.i(account.wid, account.name), buf);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Migrated %d accounts.', total);
|
||||
}
|
||||
|
||||
async function updateWallets() {
|
||||
let total = 0;
|
||||
|
||||
const iter = db.iterator({
|
||||
gte: layout.w(0),
|
||||
lte: layout.w(0xffffffff),
|
||||
values: true
|
||||
});
|
||||
|
||||
console.log('Migrating wallets.');
|
||||
|
||||
for (;;) {
|
||||
const item = await iter.next();
|
||||
|
||||
if (!item)
|
||||
break;
|
||||
|
||||
total++;
|
||||
let wallet = walletFromRaw(item.value);
|
||||
wallet = new Wallet({ network: wallet.network }, wallet);
|
||||
batch.put(item.key, wallet.toRaw());
|
||||
|
||||
if (wallet._old) {
|
||||
batch.del(layout.l(wallet._old));
|
||||
const buf = Buffer.allocUnsafe(4);
|
||||
buf.writeUInt32LE(wallet.wid, 0, true);
|
||||
batch.put(layout.l(wallet.id), buf);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Migrated %d wallets.', total);
|
||||
}
|
||||
|
||||
async function updateTXMap() {
|
||||
let total = 0;
|
||||
|
||||
const iter = db.iterator({
|
||||
gte: layout.e(encoding.NULL_HASH),
|
||||
lte: layout.e(encoding.HIGH_HASH),
|
||||
values: true
|
||||
});
|
||||
|
||||
console.log('Migrating tx map.');
|
||||
|
||||
for (;;) {
|
||||
const item = await iter.next();
|
||||
|
||||
if (!item)
|
||||
break;
|
||||
|
||||
total++;
|
||||
const wallets = parseWallets(item.value);
|
||||
batch.put(item.key, serializeWallets(wallets.sort()));
|
||||
}
|
||||
|
||||
console.log('Migrated %d tx maps.', total);
|
||||
}
|
||||
|
||||
function pathFromRaw(data) {
|
||||
const path = {};
|
||||
const p = bio.read(data);
|
||||
|
||||
path.wid = p.readU32();
|
||||
path.name = p.readVarString('utf8');
|
||||
path.account = p.readU32();
|
||||
|
||||
switch (p.readU8()) {
|
||||
case 0:
|
||||
path.keyType = 0;
|
||||
path.branch = p.readU32();
|
||||
path.index = p.readU32();
|
||||
if (p.readU8() === 1)
|
||||
assert(false, 'Cannot migrate custom redeem script.');
|
||||
break;
|
||||
case 1:
|
||||
path.keyType = 1;
|
||||
path.encrypted = p.readU8() === 1;
|
||||
path.data = p.readVarBytes();
|
||||
path.branch = -1;
|
||||
path.index = -1;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
path.version = p.readI8();
|
||||
path.type = p.readU8();
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
function parsePaths(data, hash) {
|
||||
const p = bio.read(data);
|
||||
const out = {};
|
||||
|
||||
while (p.left()) {
|
||||
const path = pathFromRaw(p);
|
||||
out[path.wid] = path;
|
||||
if (hash)
|
||||
path.hash = hash;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
function parseWallets(data) {
|
||||
const p = bio.read(data);
|
||||
const wallets = [];
|
||||
while (p.left())
|
||||
wallets.push(p.readU32());
|
||||
return wallets;
|
||||
}
|
||||
|
||||
function serializeWallets(wallets) {
|
||||
const p = bio.write();
|
||||
|
||||
for (let i = 0; i < wallets.length; i++) {
|
||||
const wid = wallets[i];
|
||||
p.writeU32(wid);
|
||||
}
|
||||
|
||||
return p.render();
|
||||
}
|
||||
|
||||
function readAccountKey(key) {
|
||||
return {
|
||||
wid: key.readUInt32BE(1, true),
|
||||
index: key.readUInt32BE(5, true)
|
||||
};
|
||||
}
|
||||
|
||||
function accountFromRaw(data, dbkey) {
|
||||
const account = {};
|
||||
const p = bio.read(data);
|
||||
|
||||
dbkey = readAccountKey(dbkey);
|
||||
account.wid = dbkey.wid;
|
||||
account.id = 'doesntmatter';
|
||||
account.network = bcoin.network.fromMagic(p.readU32());
|
||||
account.name = p.readVarString('utf8');
|
||||
account.initialized = p.readU8() === 1;
|
||||
account.type = p.readU8();
|
||||
account.m = p.readU8();
|
||||
account.n = p.readU8();
|
||||
account.witness = p.readU8() === 1;
|
||||
account.accountIndex = p.readU32();
|
||||
account.receiveDepth = p.readU32();
|
||||
account.changeDepth = p.readU32();
|
||||
account.accountKey = bcoin.hd.fromRaw(p.readBytes(82));
|
||||
account.keys = [];
|
||||
account.watchOnly = false;
|
||||
account.nestedDepth = 0;
|
||||
|
||||
const name = account.name.replace(/[^\-\._0-9A-Za-z]+/g, '');
|
||||
|
||||
if (name !== account.name) {
|
||||
console.log('Account name changed: %s -> %s.', account.name, name);
|
||||
account._old = account.name;
|
||||
account.name = name;
|
||||
}
|
||||
|
||||
const count = p.readU8();
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const key = bcoin.hd.fromRaw(p.readBytes(82));
|
||||
account.keys.push(key);
|
||||
}
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
function walletFromRaw(data) {
|
||||
const wallet = {};
|
||||
const p = bio.read(data);
|
||||
|
||||
wallet.network = bcoin.network.fromMagic(p.readU32());
|
||||
wallet.wid = p.readU32();
|
||||
wallet.id = p.readVarString('utf8');
|
||||
wallet.initialized = p.readU8() === 1;
|
||||
wallet.accountDepth = p.readU32();
|
||||
wallet.token = p.readBytes(32);
|
||||
wallet.tokenDepth = p.readU32();
|
||||
wallet.master = MasterKey.fromRaw(p.readVarBytes());
|
||||
wallet.watchOnly = false;
|
||||
|
||||
const id = wallet.id.replace(/[^\-\._0-9A-Za-z]+/g, '');
|
||||
|
||||
if (id !== wallet.id) {
|
||||
console.log('Wallet ID changed: %s -> %s.', wallet.id, id);
|
||||
wallet._old = wallet.id;
|
||||
wallet.id = id;
|
||||
}
|
||||
|
||||
return wallet;
|
||||
}
|
||||
|
||||
function keyFromRaw(data, network) {
|
||||
const ring = {};
|
||||
const p = bio.read(data);
|
||||
|
||||
ring.witness = p.readU8() === 1;
|
||||
|
||||
const key = p.readVarBytes();
|
||||
|
||||
if (key.length === 32) {
|
||||
ring.privateKey = key;
|
||||
ring.publicKey = bcoin.secp256k1.publicKeyCreate(key, true);
|
||||
} else {
|
||||
ring.publicKey = key;
|
||||
}
|
||||
|
||||
const script = p.readVarBytes();
|
||||
|
||||
if (script.length > 0)
|
||||
ring.script = bcoin.script.fromRaw(script);
|
||||
|
||||
return ring;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await db.open();
|
||||
batch = db.batch();
|
||||
console.log('Opened %s.', file);
|
||||
await updateVersion();
|
||||
await updatePathMap();
|
||||
await updateAccounts();
|
||||
await updateWallets();
|
||||
await updateTXMap();
|
||||
await batch.write();
|
||||
})().then(() => {
|
||||
console.log('Migration complete.');
|
||||
process.exit(0);
|
||||
});
|
||||
@ -1,147 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const bcoin = require('../');
|
||||
const bio = require('bufio');
|
||||
const WalletDB = require('../lib/wallet/walletdb');
|
||||
const TX = require('../lib/primitives/tx');
|
||||
const Coin = require('../lib/primitives/coin');
|
||||
const {encoding} = bio;
|
||||
|
||||
let file = process.argv[2];
|
||||
let batch;
|
||||
|
||||
assert(typeof file === 'string', 'Please pass in a database path.');
|
||||
|
||||
file = file.replace(/\.ldb\/?$/, '');
|
||||
|
||||
const db = bcoin.ldb({
|
||||
location: file,
|
||||
db: 'leveldb',
|
||||
compression: true,
|
||||
cacheSize: 32 << 20,
|
||||
createIfMissing: false,
|
||||
bufferKeys: true
|
||||
});
|
||||
|
||||
async function updateVersion() {
|
||||
const bak = `${process.env.HOME}/walletdb-bak-${Date.now()}.ldb`;
|
||||
|
||||
console.log('Checking version.');
|
||||
|
||||
const data = await db.get('V');
|
||||
assert(data, 'No version.');
|
||||
|
||||
let ver = data.readUInt32LE(0, true);
|
||||
|
||||
if (ver !== 3)
|
||||
throw Error(`DB is version ${ver}.`);
|
||||
|
||||
console.log('Backing up DB to: %s.', bak);
|
||||
|
||||
await db.backup(bak);
|
||||
|
||||
ver = Buffer.allocUnsafe(4);
|
||||
ver.writeUInt32LE(4, 0, true);
|
||||
batch.put('V', ver);
|
||||
}
|
||||
|
||||
async function updateTXDB() {
|
||||
let txs = {};
|
||||
|
||||
const keys = await db.keys({
|
||||
gte: Buffer.from([0x00]),
|
||||
lte: Buffer.from([0xff])
|
||||
});
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
if (key[0] === 0x74 && key[5] === 0x74) {
|
||||
let tx = await db.get(key);
|
||||
tx = fromExtended(tx);
|
||||
const hash = tx.hash('hex');
|
||||
txs[hash] = tx;
|
||||
}
|
||||
if (key[0] === 0x74)
|
||||
batch.del(key);
|
||||
}
|
||||
|
||||
txs = getValues(txs);
|
||||
|
||||
await batch.write();
|
||||
await db.close();
|
||||
|
||||
const walletdb = new WalletDB({
|
||||
location: file,
|
||||
db: 'leveldb',
|
||||
resolution: true,
|
||||
verify: false,
|
||||
network: process.argv[3]
|
||||
});
|
||||
|
||||
await walletdb.open();
|
||||
|
||||
for (let i = 0; i < txs.length; i++) {
|
||||
const tx = txs[i];
|
||||
await walletdb.addTX(tx);
|
||||
}
|
||||
|
||||
await walletdb.close();
|
||||
}
|
||||
|
||||
function fromExtended(data, saveCoins) {
|
||||
const tx = new TX();
|
||||
const p = bio.read(data);
|
||||
|
||||
tx.fromRaw(p);
|
||||
|
||||
tx.height = p.readU32();
|
||||
tx.block = p.readHash('hex');
|
||||
tx.index = p.readU32();
|
||||
tx.time = p.readU32();
|
||||
tx.mtime = p.readU32();
|
||||
|
||||
if (tx.block === encoding.NULL_HASH)
|
||||
tx.block = null;
|
||||
|
||||
if (tx.height === 0x7fffffff)
|
||||
tx.height = -1;
|
||||
|
||||
if (tx.index === 0x7fffffff)
|
||||
tx.index = -1;
|
||||
|
||||
if (saveCoins) {
|
||||
const coinCount = p.readVarint();
|
||||
for (let i = 0; i < coinCount; i++) {
|
||||
let coin = p.readVarBytes();
|
||||
if (coin.length === 0)
|
||||
continue;
|
||||
coin = Coin.fromRaw(coin);
|
||||
coin.hash = tx.inputs[i].prevout.hash;
|
||||
coin.index = tx.inputs[i].prevout.index;
|
||||
tx.inputs[i].coin = coin;
|
||||
}
|
||||
}
|
||||
|
||||
return tx;
|
||||
}
|
||||
|
||||
function getValues(map) {
|
||||
const items = [];
|
||||
|
||||
for (const key of Object.keys(map))
|
||||
items.push(map[key]);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await db.open();
|
||||
batch = db.batch();
|
||||
console.log('Opened %s.', file);
|
||||
await updateVersion();
|
||||
await updateTXDB();
|
||||
})().then(() => {
|
||||
console.log('Migration complete.');
|
||||
process.exit(0);
|
||||
});
|
||||
@ -1,74 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const bcoin = require('../');
|
||||
let file = process.argv[2];
|
||||
let batch;
|
||||
|
||||
assert(typeof file === 'string', 'Please pass in a database path.');
|
||||
|
||||
file = file.replace(/\.ldb\/?$/, '');
|
||||
|
||||
const db = bcoin.ldb({
|
||||
location: file,
|
||||
db: 'leveldb',
|
||||
compression: true,
|
||||
cacheSize: 32 << 20,
|
||||
createIfMissing: false,
|
||||
bufferKeys: true
|
||||
});
|
||||
|
||||
async function updateVersion() {
|
||||
const bak = `${process.env.HOME}/walletdb-bak-${Date.now()}.ldb`;
|
||||
|
||||
console.log('Checking version.');
|
||||
|
||||
const data = await db.get('V');
|
||||
assert(data, 'No version.');
|
||||
|
||||
let ver = data.readUInt32LE(0, true);
|
||||
|
||||
if (ver !== 4)
|
||||
throw Error(`DB is version ${ver}.`);
|
||||
|
||||
console.log('Backing up DB to: %s.', bak);
|
||||
|
||||
await db.backup(bak);
|
||||
|
||||
ver = Buffer.allocUnsafe(4);
|
||||
ver.writeUInt32LE(5, 0, true);
|
||||
batch.put('V', ver);
|
||||
}
|
||||
|
||||
async function updateTXDB() {
|
||||
const keys = await db.keys({
|
||||
gte: Buffer.from([0x00]),
|
||||
lte: Buffer.from([0xff])
|
||||
});
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
switch (key[0]) {
|
||||
case 0x62: // b
|
||||
case 0x63: // c
|
||||
case 0x65: // e
|
||||
case 0x74: // t
|
||||
batch.del(key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await batch.write();
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await db.open();
|
||||
batch = db.batch();
|
||||
console.log('Opened %s.', file);
|
||||
await updateVersion();
|
||||
await updateTXDB();
|
||||
await db.close();
|
||||
})().then(() => {
|
||||
console.log('Migration complete.');
|
||||
process.exit(0);
|
||||
});
|
||||
@ -1,9 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const bcoin = require('../');
|
||||
const bdb = require('bdb');
|
||||
const bio = require('bufio');
|
||||
const {encoding} = bio;
|
||||
|
||||
let file = process.argv[2];
|
||||
let batch;
|
||||
@ -12,13 +11,12 @@ assert(typeof file === 'string', 'Please pass in a database path.');
|
||||
|
||||
file = file.replace(/\.ldb\/?$/, '');
|
||||
|
||||
const db = bcoin.ldb({
|
||||
const db = bdb.create({
|
||||
location: file,
|
||||
db: 'leveldb',
|
||||
compression: true,
|
||||
cacheSize: 32 << 20,
|
||||
createIfMissing: false,
|
||||
bufferKeys: true
|
||||
createIfMissing: false
|
||||
});
|
||||
|
||||
async function updateVersion() {
|
||||
@ -26,30 +24,27 @@ async function updateVersion() {
|
||||
|
||||
console.log('Checking version.');
|
||||
|
||||
const data = await db.get('V');
|
||||
assert(data, 'No version.');
|
||||
const raw = await db.get('V');
|
||||
assert(raw, 'No version.');
|
||||
|
||||
let ver = data.readUInt32LE(0, true);
|
||||
const version = raw.readUInt32LE(0, true);
|
||||
|
||||
if (ver !== 5)
|
||||
throw Error(`DB is version ${ver}.`);
|
||||
if (version !== 5)
|
||||
throw Error(`DB is version ${version}.`);
|
||||
|
||||
console.log('Backing up DB to: %s.', bak);
|
||||
|
||||
await db.backup(bak);
|
||||
|
||||
ver = Buffer.allocUnsafe(4);
|
||||
ver.writeUInt32LE(6, 0, true);
|
||||
batch.put('V', ver);
|
||||
const data = Buffer.allocUnsafe(4);
|
||||
data.writeUInt32LE(6, 0, true);
|
||||
batch.put('V', data);
|
||||
}
|
||||
|
||||
async function wipeTXDB() {
|
||||
let total = 0;
|
||||
|
||||
const keys = await db.keys({
|
||||
gte: Buffer.from([0x00]),
|
||||
lte: Buffer.from([0xff])
|
||||
});
|
||||
const keys = await db.keys();
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
@ -61,7 +56,7 @@ async function wipeTXDB() {
|
||||
case 0x6f: // o
|
||||
case 0x68: // h
|
||||
batch.del(key);
|
||||
total++;
|
||||
total += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -73,8 +68,8 @@ async function wipeTXDB() {
|
||||
|
||||
async function patchAccounts() {
|
||||
const items = await db.range({
|
||||
gte: Buffer.from('610000000000000000', 'hex'), // a
|
||||
lte: Buffer.from('61ffffffffffffffff', 'hex') // a
|
||||
gt: Buffer.from([0x61]), // a
|
||||
lt: Buffer.from([0x62])
|
||||
});
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
@ -91,8 +86,8 @@ async function patchAccounts() {
|
||||
|
||||
async function indexPaths() {
|
||||
const items = await db.range({
|
||||
gte: Buffer.from('5000000000' + encoding.NULL_HASH, 'hex'), // P
|
||||
lte: Buffer.from('50ffffffff' + encoding.HIGH_HASH, 'hex') // P
|
||||
gt: Buffer.from([0x50]), // P
|
||||
lt: Buffer.from([0x51])
|
||||
});
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
@ -107,8 +102,8 @@ async function indexPaths() {
|
||||
|
||||
async function patchPathMaps() {
|
||||
const items = await db.range({
|
||||
gte: Buffer.from('70' + encoding.NULL_HASH, 'hex'), // p
|
||||
lte: Buffer.from('70' + encoding.HIGH_HASH, 'hex') // p
|
||||
gt: Buffer.from([0x70]), // p
|
||||
lt: Buffer.from([0x71])
|
||||
});
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
@ -261,7 +256,7 @@ async function unstate() {
|
||||
await db.close();
|
||||
|
||||
// Do not use:
|
||||
await updateLookahead();
|
||||
// await updateLookahead();
|
||||
await unstate();
|
||||
})().then(() => {
|
||||
console.log('Migration complete.');
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const consensus = require('../lib/protocol/consensus');
|
||||
const {encoding} = require('bufio');
|
||||
const TX = require('../lib/primitives/tx');
|
||||
const Block = require('../lib/primitives/block');
|
||||
const Script = require('../lib/script/script');
|
||||
@ -31,7 +30,7 @@ function createGenesisBlock(options) {
|
||||
version: 1,
|
||||
inputs: [{
|
||||
prevout: {
|
||||
hash: encoding.NULL_HASH,
|
||||
hash: consensus.NULL_HASH,
|
||||
index: 0xffffffff
|
||||
},
|
||||
script: Script()
|
||||
@ -50,7 +49,7 @@ function createGenesisBlock(options) {
|
||||
|
||||
const block = new Block({
|
||||
version: options.version,
|
||||
prevBlock: encoding.NULL_HASH,
|
||||
prevBlock: consensus.NULL_HASH,
|
||||
merkleRoot: tx.hash('hex'),
|
||||
time: options.time,
|
||||
bits: options.bits,
|
||||
|
||||
@ -10,7 +10,6 @@ const Block = require('../lib/primitives/block');
|
||||
const MerkleBlock = require('../lib/primitives/merkleblock');
|
||||
const consensus = require('../lib/protocol/consensus');
|
||||
const Script = require('../lib/script/script');
|
||||
const {encoding} = require('bufio');
|
||||
const bip152 = require('../lib/net/bip152');
|
||||
const CompactBlock = bip152.CompactBlock;
|
||||
const TXRequest = bip152.TXRequest;
|
||||
@ -158,7 +157,7 @@ describe('Block', function() {
|
||||
it('should fail with a bad merkle root', () => {
|
||||
const [block] = block300025.getBlock();
|
||||
const merkleRoot = block.merkleRoot;
|
||||
block.merkleRoot = encoding.NULL_HASH;
|
||||
block.merkleRoot = consensus.NULL_HASH;
|
||||
block.refresh();
|
||||
assert(!block.verifyPOW());
|
||||
const [, reason] = block.checkBody();
|
||||
@ -172,7 +171,7 @@ describe('Block', function() {
|
||||
it('should fail on merkle block with a bad merkle root', () => {
|
||||
const [block] = merkle300025.getBlock();
|
||||
const merkleRoot = block.merkleRoot;
|
||||
block.merkleRoot = encoding.NULL_HASH;
|
||||
block.merkleRoot = consensus.NULL_HASH;
|
||||
block.refresh();
|
||||
assert(!block.verifyPOW());
|
||||
const [, reason] = block.checkBody();
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
const {encoding} = require('bufio');
|
||||
const assert = require('./util/assert');
|
||||
const consensus = require('../lib/protocol/consensus');
|
||||
const Address = require('../lib/primitives/address');
|
||||
@ -76,7 +75,7 @@ describe('HTTP', function() {
|
||||
|
||||
it('should fill with funds', async () => {
|
||||
const mtx = new MTX();
|
||||
mtx.addOutpoint(new Outpoint(encoding.NULL_HASH, 0));
|
||||
mtx.addOutpoint(new Outpoint(consensus.NULL_HASH, 0));
|
||||
mtx.addOutput(addr, 50460);
|
||||
mtx.addOutput(addr, 50460);
|
||||
mtx.addOutput(addr, 50460);
|
||||
|
||||
@ -9,7 +9,7 @@ const Witness = require('../lib/script/witness');
|
||||
const Stack = require('../lib/script/stack');
|
||||
const Opcode = require('../lib/script/opcode');
|
||||
const TX = require('../lib/primitives/tx');
|
||||
const {encoding} = require('bufio');
|
||||
const consensus = require('../lib/protocol/consensus');
|
||||
const {fromFloat} = require('../lib/utils/fixed');
|
||||
|
||||
const scripts = require('./data/script-tests.json');
|
||||
@ -286,7 +286,7 @@ describe('Script', function() {
|
||||
version: 1,
|
||||
inputs: [{
|
||||
prevout: {
|
||||
hash: encoding.NULL_HASH,
|
||||
hash: consensus.NULL_HASH,
|
||||
index: 0xffffffff
|
||||
},
|
||||
script: [
|
||||
|
||||
@ -692,7 +692,7 @@ describe('TX', function() {
|
||||
const output = Script.fromProgram(0, key.getKeyHash());
|
||||
const ctx = sigopContext(input, witness, output);
|
||||
|
||||
ctx.spend.inputs[0].prevout.hash = encoding.NULL_HASH;
|
||||
ctx.spend.inputs[0].prevout.hash = consensus.NULL_HASH;
|
||||
ctx.spend.inputs[0].prevout.index = 0xffffffff;
|
||||
ctx.spend.refresh();
|
||||
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
const {encoding} = require('bufio');
|
||||
const assert = require('./util/assert');
|
||||
const consensus = require('../lib/protocol/consensus');
|
||||
const util = require('../lib/utils/util');
|
||||
@ -679,7 +678,7 @@ describe('Wallet', function() {
|
||||
|
||||
// Coinbase
|
||||
const t1 = new MTX();
|
||||
t1.addOutpoint(new Outpoint(encoding.NULL_HASH, 0));
|
||||
t1.addOutpoint(new Outpoint(consensus.NULL_HASH, 0));
|
||||
t1.addOutput(await alice.receiveAddress(), 5460);
|
||||
t1.addOutput(await alice.receiveAddress(), 5460);
|
||||
t1.addOutput(await alice.receiveAddress(), 5460);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user