Skip to content

Commit

Permalink
walletdb: Write time with blockmeta record. Update wdb version to 3.
Browse files Browse the repository at this point in the history
This requires full wdb block entry wipe and rescan. That is handled by
PR handshake-org#889. `layout.h` is looked up by height, so only missing data was
time. Now we can implement walletdb only median time past calculation.
  • Loading branch information
nodech committed Aug 29, 2024
1 parent c38e4b9 commit f233b76
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 46 deletions.
13 changes: 13 additions & 0 deletions lib/wallet/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@ class WalletClient extends NodeClient {
return parseEntry(await super.getEntry(block));
}

/**
* Get entries.
* @param {Number} [start=-1]
* @param {Number} [end=-1]
* @returns {Promise<Object[]>}
*/

async getEntries(start, end) {
const entries = await super.getEntries(start, end);
return entries.map(parseEntry);
}

async send(tx) {
return super.send(tx.encode());
}
Expand Down Expand Up @@ -138,6 +150,7 @@ function parseEntry(data) {

const hash = data.slice(0, 32);
const height = encoding.readU32(data, 32);
// skip nonce 4.
const time = encoding.readU64(data, 40);
const prevBlock = data.slice(48, 80);

Expand Down
11 changes: 11 additions & 0 deletions lib/wallet/nodeclient.js
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,17 @@ class NodeClient extends AsyncEmitter {
return this.node.chain.getHashes(start, end);
}

/**
* Get entries range.
* @param {Number} start
* @param {Number} end
* @returns {Promise<ChainEntry[]>}
*/

async getEntries(start = -1, end = -1) {
return this.node.chain.getEntries(start, end);
}

/**
* Rescan for any missed transactions.
* @param {Number|Hash} start - Start block.
Expand Down
18 changes: 18 additions & 0 deletions lib/wallet/nullclient.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
const assert = require('bsert');
const EventEmitter = require('events');
const NameState = require('../covenants/namestate');
const Block = require('../primitives/block');
const util = require('../utils/util');

/**
Expand Down Expand Up @@ -173,6 +174,23 @@ class NullClient extends EventEmitter {
return [this.network.genesis.hash];
}

/**
* Get entries.
* @param {Number} [start=-1]
* @param {Number} [end=-1]
* @returns {Promise}
*/

async getEntries(start = -1, end = -1) {
const genesisBlock = Block.decode(this.network.genesisBlock);
const entry = {
hash: genesisBlock.hash(),
height: 0,
time: genesisBlock.time
};
return [entry];
}

/**
* Rescan for any missed transactions.
* @param {Number|Hash} start - Start block.
Expand Down
62 changes: 44 additions & 18 deletions lib/wallet/records.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,40 @@ class BlockMeta extends bio.Struct {
}

/**
* Get block meta hash as a buffer.
* Encode hash and time.
* @returns {Buffer}
*/

toHash() {
return this.hash;
toHashAndTime() {
const data = Buffer.allocUnsafe(32 + 8);
bio.writeBytes(data, this.hash, 0);
bio.writeU64(data, this.time, 32);
return data;
}

/**
* Decode hash and time.
* @param {Buffer} data
* @param {Number} height
* @returns {BlockMeta}
*/

fromHashAndTime(data, height) {
this.hash = data.slice(0, 32);
this.time = bio.readU64(data, 32);
this.height = height;
return this;
}

/**
* Instantiate block meta from hash and time.
* @param {Buffer} data
* @param {Number} height
* @returns {BlockMeta}
*/

static fromHashAndTime(data, height) {
return new this().fromHashAndTime(data, height);
}

/**
Expand All @@ -139,6 +167,16 @@ class BlockMeta extends bio.Struct {
return this;
}

/**
* Instantiate block meta from chain entry.
* @param {ChainEntry} entry
* @returns {BlockMeta}
*/

static fromEntry(entry) {
return new this().fromEntry(entry);
}

/**
* Instantiate block meta from serialized tip data.
* @private
Expand All @@ -148,27 +186,17 @@ class BlockMeta extends bio.Struct {
read(br) {
this.hash = br.readHash();
this.height = br.readU32();
this.time = br.readU32();
this.time = br.readU64();
return this;
}

/**
* Instantiate block meta from chain entry.
* @param {ChainEntry} entry
* @returns {BlockMeta}
*/

static fromEntry(entry) {
return new this().fromEntry(entry);
}

/**
* Calculate size.
* @returns {Number}
*/

getSize() {
return 40;
return 44;
}

/**
Expand All @@ -179,7 +207,7 @@ class BlockMeta extends bio.Struct {
write(bw) {
bw.writeHash(this.hash);
bw.writeU32(this.height);
bw.writeU32(this.time);
bw.writeU64(this.time);
return bw;
}

Expand Down Expand Up @@ -462,5 +490,3 @@ exports.ChainState = ChainState;
exports.BlockMeta = BlockMeta;
exports.TXRecord = TXRecord;
exports.MapRecord = MapRecord;

module.exports = exports;
45 changes: 35 additions & 10 deletions lib/wallet/walletdb.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ const Logger = require('blgr');
const {safeEqual} = require('bcrypto/lib/safe');
const aes = require('bcrypto/lib/aes');
const Network = require('../protocol/network');
const consensus = require('../protocol/consensus');
const Path = require('./path');
const common = require('./common');
const Wallet = require('./wallet');
const Account = require('./account');
const Block = require('../primitives/block');
const Outpoint = require('../primitives/outpoint');
const layouts = require('./layout');
const records = require('./records');
Expand Down Expand Up @@ -342,6 +344,25 @@ class WalletDB extends EventEmitter {
return undefined;
}

/**
* Add genesis block.
* @returns {Promise}
*/

async saveGenesis() {
// Write genesis block.
const network = this.network;
const block = Block.decode(network.genesisBlock);
const entry = {
hash: block.hash(),
height: 0,
time: block.time,
prevBlock: consensus.ZERO_HASH
};

await this.addBlock(entry, []);
}

/**
* Close the walletdb, wait for the database to close.
* @returns {Promise}
Expand Down Expand Up @@ -453,6 +474,7 @@ class WalletDB extends EventEmitter {
await this.syncInitState();
await this.syncFilter();
await this.syncChain();
this.rescanning = false;
await this.resend();
} finally {
this.rescanning = false;
Expand All @@ -468,8 +490,10 @@ class WalletDB extends EventEmitter {
async loadState() {
const cache = await this.getState();

if (!cache)
if (!cache) {
await this.saveGenesis();
return;
}

this.logger.info('Initialized chain state from the database.');
this.hasStateCache = true;
Expand All @@ -490,14 +514,15 @@ class WalletDB extends EventEmitter {
this.logger.info('Initializing database state from server.');

const b = this.db.batch();
const hashes = await this.client.getHashes();
const entries = await this.client.getEntries();

let tip = null;

for (let height = 0; height < hashes.length; height++) {
const hash = hashes[height];
const meta = new BlockMeta(hash, height);
b.put(layout.h.encode(height), meta.toHash());
for (let height = 0; height < entries.length; height++) {
const entry = entries[height];
assert(entry.height === height);
const meta = new BlockMeta(entry.hash, entry.height, entry.time);
b.put(layout.h.encode(height), meta.toHashAndTime());
tip = meta;
}

Expand Down Expand Up @@ -1969,7 +1994,7 @@ class WalletDB extends EventEmitter {
}

// Save tip and state.
b.put(layout.h.encode(tip.height), tip.toHash());
b.put(layout.h.encode(tip.height), tip.toHashAndTime());
b.put(layout.R.encode(), state.encode());

await b.write();
Expand Down Expand Up @@ -2257,12 +2282,12 @@ class WalletDB extends EventEmitter {
*/

async getBlock(height) {
const hash = await this.db.get(layout.h.encode(height));
const data = await this.db.get(layout.h.encode(height));

if (!hash)
if (!data)
return null;

return new BlockMeta(hash, height);
return BlockMeta.fromHashAndTime(data, height);
}

/**
Expand Down
Loading

0 comments on commit f233b76

Please sign in to comment.