Merge PR #657 from 'nodech/wallet-record-blockmeta'

This commit is contained in:
Nodari Chkuaselidze 2021-11-30 18:12:10 +04:00
commit eea15c2f2d
No known key found for this signature in database
GPG key ID: B018A7BB437D1F05
3 changed files with 440 additions and 20 deletions

View file

@ -139,19 +139,6 @@ class BlockMeta extends bio.Struct {
return this;
}
/**
* Instantiate block meta from json object.
* @private
* @param {Object} json
*/
fromJSON(json) {
this.hash = json.hash;
this.height = json.height;
this.time = json.time;
return this;
}
/**
* Instantiate block meta from serialized tip data.
* @private
@ -181,7 +168,7 @@ class BlockMeta extends bio.Struct {
*/
getSize() {
return 42;
return 40;
}
/**
@ -196,6 +183,18 @@ class BlockMeta extends bio.Struct {
return bw;
}
/**
* Instantiate block meta from json object.
* @param {Object} json
*/
fromJSON(json) {
this.hash = util.parseHex(json.hash, 32);
this.height = json.height;
this.time = json.time;
return this;
}
/**
* Convert the block meta to a more json-friendly object.
* @returns {Object}
@ -203,7 +202,7 @@ class BlockMeta extends bio.Struct {
getJSON() {
return {
hash: this.hash,
hash: this.hash.toString('hex'),
height: this.height,
time: this.time
};

View file

@ -2115,11 +2115,7 @@ class WalletDB extends EventEmitter {
if (!hash)
return null;
const block = new BlockMeta();
block.hash = hash;
block.height = height;
return block;
return new BlockMeta(hash, height);
}
/**

425
test/wallet-records-test.js Normal file
View file

@ -0,0 +1,425 @@
'use strict';
const assert = require('bsert');
const random = require('bcrypto/lib/random');
const consensus = require('../lib/protocol/consensus');
const Records = require('../lib/wallet/records');
const ChainEntry = require('../lib/blockchain/chainentry');
const TX = require('../lib/primitives/tx');
const {
ChainState,
BlockMeta,
TXRecord,
MapRecord
} = Records;
function getRandomChainState(marked = false) {
return {
startHeight: random.randomInt(),
startHash: random.randomBytes(32),
height: random.randomInt(),
marked: marked
};
}
function getRandomBlockMetaData() {
return {
hash: random.randomBytes(32),
height: random.randomInt(),
time: random.randomInt()
};
}
function getRandomTX() {
return new TX({
inputs: [{
prevout: {
hash: random.randomBytes(32),
index: random.randomRange(0, 100)
}
}],
outputs: [{
value: random.randomInt()
}]
});
}
function getRandomTXRecordData(genTX = false, genBlock = false) {
let block, tx;
if (genBlock) {
const blockData = getRandomBlockMetaData();
block = new BlockMeta(blockData.hash, blockData.height, blockData.time);
}
if (genTX)
tx = getRandomTX();
const data = {
height: block ? block.height : -1,
time: block ? block.time : 0,
block: block ? block.hash : null,
tx: tx,
hash: tx ? tx.hash() : null
};
return {data, tx, block};
}
/*
* These don't expect actual instances of these classes
* just object property checks.
*/
function compareChainState(state, expected) {
assert.strictEqual(state.startHeight, expected.startHeight);
assert.bufferEqual(state.startHash, expected.startHash);
assert.strictEqual(state.height, expected.height);
assert.strictEqual(state.marked, expected.marked);
};
function compareBlockMeta(actual, expected) {
assert.bufferEqual(actual.hash, expected.hash);
assert.strictEqual(actual.height, expected.height);
assert.strictEqual(actual.time, expected.time);
}
function compareTXRecord(actual, expected) {
if (actual.tx == null) {
// defaults
assert.strictEqual(actual.hash, null);
// expected should be the same (otherwise it's a bug in the test).
assert.strictEqual(actual.tx, expected.tx);
assert.strictEqual(actual.hash, expected.hash);
} else {
assert.bufferEqual(actual.hash, expected.hash);
assert.bufferEqual(actual.tx.encode(), expected.tx.encode());
}
if (actual.block == null) {
// same as actual.tx checks
assert.strictEqual(actual.height, -1);
assert.strictEqual(actual.time, 0);
assert.strictEqual(actual.block, expected.block);
assert.strictEqual(actual.height, expected.height);
assert.strictEqual(actual.time, expected.time);
} else {
assert.bufferEqual(actual.block, expected.block);
assert.strictEqual(actual.height, expected.height);
assert.strictEqual(actual.time, expected.time);
}
assert(typeof actual.mtime === 'number');
assert(actual.mtime > 0);
assert.strictEqual(actual.index, -1);
}
describe('Wallet Records', function() {
describe('ChainState', function() {
it('should encode/decode with defaults', () => {
const state = new ChainState();
const encoded = state.encode();
const decoded = ChainState.decode(encoded);
assert.deepStrictEqual(state, decoded);
compareChainState(state, {
startHeight: 0,
startHash: consensus.ZERO_HASH,
height: 0,
marked: false
});
});
it('should inject data', () => {
const data1 = getRandomChainState(false);
const data2 = getRandomChainState(true);
let state = new ChainState();
state.inject(data1);
compareChainState(state, data1);
state = new ChainState();
state.inject(data2);
compareChainState(state, data2);
});
it('should encode/decode', () => {
const data = getRandomChainState(false);
const state = new ChainState();
state.inject(data);
const encoded = state.encode();
const decoded = ChainState.decode(encoded);
compareChainState(decoded, state);
});
it('should encode/decode with marked true', () => {
const data = getRandomChainState(true);
const state = new ChainState();
state.inject(data);
const encoded = state.encode();
const decoded = ChainState.decode(encoded);
compareChainState(decoded, state);
});
});
describe('BlockMeta', function() {
it('should initialize with defaults', () => {
const meta = new BlockMeta();
compareBlockMeta(meta, {
hash: consensus.ZERO_HASH,
height: -1,
time: 0
});
});
it('should initialize with params', () => {
const data = getRandomBlockMetaData();
const meta = new BlockMeta(data.hash, data.height, data.time);
compareBlockMeta(meta, data);
});
it('should inject data', () => {
const data = getRandomBlockMetaData();
const meta = new BlockMeta();
meta.inject(data);
compareBlockMeta(meta, data);
});
it('should encode/decode', () => {
const data = getRandomBlockMetaData();
const meta = new BlockMeta();
meta.inject(data);
const encoded = meta.encode();
const decoded = BlockMeta.decode(encoded);
compareBlockMeta(meta, data);
compareBlockMeta(decoded, meta);
});
it('should JSON encode/decode', () => {
const data = getRandomBlockMetaData();
const meta = new BlockMeta();
meta.inject(data);
const encoded = meta.toJSON();
const decoded = BlockMeta.fromJSON(encoded);
assert.deepStrictEqual(encoded, {
hash: data.hash.toString('hex'),
height: data.height,
time: data.time
});
compareBlockMeta(decoded, data);
});
it('should return block hash', () => {
const data = getRandomBlockMetaData();
const meta = new BlockMeta();
meta.inject(data);
assert.bufferEqual(meta.toHash(), data.hash);
});
it('should be created from ChainEntry', () => {
const data = getRandomBlockMetaData();
const entry = new ChainEntry();
entry.hash = data.hash;
entry.height = data.height;
entry.time = data.time;
const meta = BlockMeta.fromEntry(entry);
compareBlockMeta(meta, data);
});
});
describe('TXRecord', function() {
const emptyBlock = {
height: -1,
block: null,
time: 0
};
const emptyTX = {
tx: null,
hash: null
};
it('should initialize with defaults', () => {
const wtx = new TXRecord();
const mtime = wtx.mtime;
compareTXRecord(wtx, {
mtime: mtime,
index: -1,
...emptyTX,
...emptyBlock
});
});
it('should initialize w/ tx', () => {
const {data, tx} = getRandomTXRecordData(true);
const wtx = new TXRecord(tx);
compareTXRecord(wtx, data);
});
it('should initialize w/ tx and block', () => {
const {data, block, tx} = getRandomTXRecordData(true, true);
const wtx = new TXRecord(tx, block);
compareTXRecord(wtx, data);
});
it('should fail encode w/o tx', () => {
let err;
try {
const wtx = new TXRecord();
wtx.encode();
} catch (e) {
err = e;
}
assert(err, 'Should throw error w/o tx');
});
it('should encode/decode w/ tx', () => {
const {data, tx} = getRandomTXRecordData(true);
const wtx = new TXRecord(tx);
const encoded = wtx.encode();
const decoded = TXRecord.decode(encoded);
compareTXRecord(wtx, data);
compareTXRecord(decoded, data);
});
it('should encode/decode w/ tx and block', () => {
const {data, tx, block} = getRandomTXRecordData(true, true);
const wtx = new TXRecord(tx, block);
const encoded = wtx.encode();
const decoded = TXRecord.decode(encoded);
compareTXRecord(wtx, data);
compareTXRecord(decoded, data);
});
it('should initialize from TX', () => {
const {data, tx} = getRandomTXRecordData(true);
const wtx = TXRecord.fromTX(tx);
compareTXRecord(wtx, data);
});
it('should initialize from TX and Block', () => {
const {data, tx, block} = getRandomTXRecordData(true, true);
const wtx = TXRecord.fromTX(tx, block);
compareTXRecord(wtx, data);
});
it('should set and unset block', () => {
const {data, tx, block} = getRandomTXRecordData(true, true);
const wtx = TXRecord.fromTX(tx);
assert.strictEqual(wtx.getBlock(), null);
assert.strictEqual(wtx.getDepth(random.randomInt()), 0);
compareTXRecord(wtx, {
...data,
...emptyBlock
});
wtx.setBlock(block);
assert.strictEqual(wtx.getDepth(0), 0);
assert.strictEqual(wtx.getDepth(block.height), 1);
assert.strictEqual(wtx.getDepth(block.height + 1000), 1001);
compareBlockMeta(wtx.getBlock(), block);
compareTXRecord(wtx, data);
wtx.unsetBlock(block);
assert.strictEqual(wtx.getBlock(), null);
assert.strictEqual(wtx.getDepth(random.randomInt()), 0);
compareTXRecord(wtx, {
...data,
...emptyBlock
});
});
});
describe('MapRecord', function() {
it('should initialize with default', () => {
const map = new MapRecord();
assert.strictEqual(map.wids.size, 0);
});
it('should encode/decode empty map', () => {
const map = new MapRecord();
const encoded = map.encode();
const decoded = MapRecord.decode(encoded);
assert.bufferEqual(encoded, Buffer.from('00'.repeat(4), 'hex'));
assert.strictEqual(map.wids.size, 0);
assert.strictEqual(decoded.wids.size, 0);
});
it('should encode/decode map', () => {
const rand = random.randomRange(1, 100);
const map = new MapRecord();
for (let i = 0; i < rand; i++)
map.add(i);
const encoded = map.encode();
const decoded = MapRecord.decode(encoded);
assert.strictEqual(decoded.wids.size, map.wids.size);
for (let i = 0; i < rand; i++)
assert(decoded.wids.has(i));
});
it('should add and remove items from the map', () => {
const items = 20;
const map = new MapRecord();
for (let i = 0; i < items; i++) {
const res = map.add(i);
assert.strictEqual(res, true);
assert.strictEqual(map.wids.size, i + 1);
}
for (let i = 0; i < items; i++) {
const res = map.add(i);
assert.strictEqual(res, false);
assert.strictEqual(map.wids.size, items);
}
for (let i = items; i < items * 2; i++) {
const res = map.remove(i);
assert.strictEqual(res, false);
assert.strictEqual(map.wids.size, items);
}
for (let i = 0; i < items; i++) {
const res = map.remove(i);
assert.strictEqual(res, true);
assert.strictEqual(map.wids.size, items - i - 1);
}
for (let i = 0; i < items; i++) {
const res = map.remove(i);
assert.strictEqual(res, false);
assert.strictEqual(map.wids.size, 0);
}
});
});
});