diff --git a/bench/tx.js b/bench/tx.js index 68e86623..32a975fc 100644 --- a/bench/tx.js +++ b/bench/tx.js @@ -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() diff --git a/lib/blockchain/chaindb.js b/lib/blockchain/chaindb.js index fed78df6..9dd92294 100644 --- a/lib/blockchain/chaindb.js +++ b/lib/blockchain/chaindb.js @@ -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; diff --git a/lib/blockchain/chainentry.js b/lib/blockchain/chainentry.js index 3d61e60e..60362919 100644 --- a/lib/blockchain/chainentry.js +++ b/lib/blockchain/chainentry.js @@ -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; diff --git a/lib/hd/private.js b/lib/hd/private.js index 3f7ad833..00ac4691 100644 --- a/lib/hd/private.js +++ b/lib/hd/private.js @@ -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; diff --git a/lib/hd/public.js b/lib/hd/public.js index 3df86c48..9bc1796f 100644 --- a/lib/hd/public.js +++ b/lib/hd/public.js @@ -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; diff --git a/lib/mempool/layout.js b/lib/mempool/layout.js index 1914595c..81fddc34 100644 --- a/lib/mempool/layout.js +++ b/lib/mempool/layout.js @@ -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']) diff --git a/lib/mempool/mempool.js b/lib/mempool/mempool.js index 28c3f802..f4bd0481 100644 --- a/lib/mempool/mempool.js +++ b/lib/mempool/mempool.js @@ -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()); diff --git a/lib/mining/template.js b/lib/mining/template.js index ffb25b1e..bb781565 100644 --- a/lib/mining/template.js +++ b/lib/mining/template.js @@ -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]); diff --git a/lib/net/bip150.js b/lib/net/bip150.js index 97645547..d3d1090b 100644 --- a/lib/net/bip150.js +++ b/lib/net/bip150.js @@ -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; diff --git a/lib/net/bip152.js b/lib/net/bip152.js index 65572484..af512fde 100644 --- a/lib/net/bip152.js +++ b/lib/net/bip152.js @@ -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) diff --git a/lib/net/packets.js b/lib/net/packets.js index 0b1d41c4..ed51797f 100644 --- a/lib/net/packets.js +++ b/lib/net/packets.js @@ -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; } /** diff --git a/lib/node/rpc.js b/lib/node/rpc.js index 9ff36495..b84d0a22 100644 --- a/lib/node/rpc.js +++ b/lib/node/rpc.js @@ -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 diff --git a/lib/primitives/abstractblock.js b/lib/primitives/abstractblock.js index 756a2d5e..7cd1e709 100644 --- a/lib/primitives/abstractblock.js +++ b/lib/primitives/abstractblock.js @@ -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; diff --git a/lib/primitives/address.js b/lib/primitives/address.js index 6b71b103..a82f3a38 100644 --- a/lib/primitives/address.js +++ b/lib/primitives/address.js @@ -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) diff --git a/lib/primitives/block.js b/lib/primitives/block.js index d6a13cb6..66bc40ed 100644 --- a/lib/primitives/block.js +++ b/lib/primitives/block.js @@ -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]; diff --git a/lib/primitives/coin.js b/lib/primitives/coin.js index 994a79a2..f51d19f3 100644 --- a/lib/primitives/coin.js +++ b/lib/primitives/coin.js @@ -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) diff --git a/lib/primitives/merkleblock.js b/lib/primitives/merkleblock.js index 57b93bca..72f92f63 100644 --- a/lib/primitives/merkleblock.js +++ b/lib/primitives/merkleblock.js @@ -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(); diff --git a/lib/primitives/outpoint.js b/lib/primitives/outpoint.js index 1ee4e6b4..326bfcc8 100644 --- a/lib/primitives/outpoint.js +++ b/lib/primitives/outpoint.js @@ -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; } /** diff --git a/lib/primitives/tx.js b/lib/primitives/tx.js index ad357dc6..ee81d340 100644 --- a/lib/primitives/tx.js +++ b/lib/primitives/tx.js @@ -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) { diff --git a/lib/protocol/consensus.js b/lib/protocol/consensus.js index e61182f9..d144108a 100644 --- a/lib/protocol/consensus.js +++ b/lib/protocol/consensus.js @@ -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. diff --git a/lib/wallet/records.js b/lib/wallet/records.js index 04e09527..24521467 100644 --- a/lib/wallet/records.js +++ b/lib/wallet/records.js @@ -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; } diff --git a/lib/wallet/rpc.js b/lib/wallet/rpc.js index 3fc700dc..0a3bd3cf 100644 --- a/lib/wallet/rpc.js +++ b/lib/wallet/rpc.js @@ -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 }; } diff --git a/lib/wallet/txdb.js b/lib/wallet/txdb.js index 8fcd4020..50932de8 100644 --- a/lib/wallet/txdb.js +++ b/lib/wallet/txdb.js @@ -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(); diff --git a/lib/wallet/wallet.js b/lib/wallet/wallet.js index 918d3f15..0325f9b3 100644 --- a/lib/wallet/wallet.js +++ b/lib/wallet/wallet.js @@ -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(); diff --git a/migrate/chaindb0to1.js b/migrate/chaindb0to1.js deleted file mode 100644 index 2c61cc69..00000000 --- a/migrate/chaindb0to1.js +++ /dev/null @@ -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); -}); diff --git a/migrate/chaindb1to2.js b/migrate/chaindb1to2.js deleted file mode 100644 index 1d71e424..00000000 --- a/migrate/chaindb1to2.js +++ /dev/null @@ -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); -}); diff --git a/migrate/chaindb2to3.js b/migrate/chaindb2to3.js index 6dbc87d0..94853469 100644 --- a/migrate/chaindb2to3.js +++ b/migrate/chaindb2to3.js @@ -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(); diff --git a/migrate/coins-old.js b/migrate/coins-old.js deleted file mode 100644 index 02f056de..00000000 --- a/migrate/coins-old.js +++ /dev/null @@ -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; diff --git a/migrate/coinview-old.js b/migrate/coinview-old.js deleted file mode 100644 index 4d363921..00000000 --- a/migrate/coinview-old.js +++ /dev/null @@ -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; diff --git a/migrate/compress-old.js b/migrate/compress-old.js deleted file mode 100644 index 83598864..00000000 --- a/migrate/compress-old.js +++ /dev/null @@ -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 -}; diff --git a/migrate/ensure-tip-index.js b/migrate/ensure-tip-index.js deleted file mode 100644 index 84aa5ff2..00000000 --- a/migrate/ensure-tip-index.js +++ /dev/null @@ -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); -}); diff --git a/migrate/walletdb2to3.js b/migrate/walletdb2to3.js deleted file mode 100644 index 87e79fad..00000000 --- a/migrate/walletdb2to3.js +++ /dev/null @@ -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); -}); diff --git a/migrate/walletdb3to4.js b/migrate/walletdb3to4.js deleted file mode 100644 index feba9e34..00000000 --- a/migrate/walletdb3to4.js +++ /dev/null @@ -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); -}); diff --git a/migrate/walletdb4to5.js b/migrate/walletdb4to5.js deleted file mode 100644 index 9a767607..00000000 --- a/migrate/walletdb4to5.js +++ /dev/null @@ -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); -}); diff --git a/migrate/walletdb5to6.js b/migrate/walletdb5to6.js index 602bc36b..eec19c3c 100644 --- a/migrate/walletdb5to6.js +++ b/migrate/walletdb5to6.js @@ -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.'); diff --git a/scripts/gen.js b/scripts/gen.js index 2a111cc7..76b4e1b7 100644 --- a/scripts/gen.js +++ b/scripts/gen.js @@ -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, diff --git a/test/block-test.js b/test/block-test.js index 40c7705c..fbde9271 100644 --- a/test/block-test.js +++ b/test/block-test.js @@ -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(); diff --git a/test/http-test.js b/test/http-test.js index cedd1727..13c3a82c 100644 --- a/test/http-test.js +++ b/test/http-test.js @@ -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); diff --git a/test/script-test.js b/test/script-test.js index ad9c6579..b4ae3e46 100644 --- a/test/script-test.js +++ b/test/script-test.js @@ -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: [ diff --git a/test/tx-test.js b/test/tx-test.js index ca1aad38..2b38b9f4 100644 --- a/test/tx-test.js +++ b/test/tx-test.js @@ -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(); diff --git a/test/wallet-test.js b/test/wallet-test.js index f5d045b4..c6aef3c5 100644 --- a/test/wallet-test.js +++ b/test/wallet-test.js @@ -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);