Skip to content
This repository has been archived by the owner on Oct 19, 2022. It is now read-only.

Commit

Permalink
Sync validation (#18)
Browse files Browse the repository at this point in the history
* update deps

* update package lock

* rem mnListDiff

* add isValidTimestamp fn to consensus

* add timestamp check

* Timestamps: median instead of avg

* update dark-gravity-wave

* bump minor version

* update dashcore-lib dep

* update package lock

* merge changes from master

* update consensus

* rem mnlist merkleproofs & test

* correct validProofOfWork

* rem unnecessary property .pow

* typos in tests

* add deserialized raw headers for tests

* add network argument

* add network property

* check hasValidTarget only when enough headers

* fix tests with deserialized headers

* fix serialized tests

* update switch case devnet

* tests for adding many headers
  • Loading branch information
Cofresi authored May 23, 2019
1 parent affc4c6 commit 75b421d
Show file tree
Hide file tree
Showing 11 changed files with 5,281 additions and 273 deletions.
11 changes: 11 additions & 0 deletions config/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ module.exports = {
nonce: 3861367235,
});
},
getDevnetGenesis() {
// Custom genesis to test with lower difficulty
return utils.normalizeHeader({
version: 1,
previousblockhash: '0000000000000000000000000000000000000000000000000000000000000000',
merkleroot: 'e0028eb9648db56b1ac77cf090b99048a8007e2bb64b68f092c03c7f56a662c7',
time: 1390666206,
bits: '1e0ffff0',
nonce: 3861367235,
});
},
getLivenetGenesis() {
throw Error('Livenet genesis not yet implemented');
},
Expand Down
43 changes: 38 additions & 5 deletions lib/consensus.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,44 @@
const { isValidTarget } = require('@dashevo/dark-gravity-wave');
const { hasValidTarget } = require('@dashevo/dark-gravity-wave');
const utils = require('./utils');

module.exports = {
const MIN_TIMESTAMP_HEADERS = 11;
const MIN_DGW_HEADERS = 24;

function getMedianTimestamp(headers) {
const timestamps = headers.map(h => h.time);
const median = (arr) => {
const mid = Math.floor(arr.length / 2);
const nums = [...arr].sort((a, b) => a - b);
return arr.length % 2 !== 0 ? nums[mid] : (nums[mid - 1] + nums[mid]) / 2;
};
return median(timestamps);
}

// Must be strictly greater than the median time of the previous 11 blocks.
// https://dash-docs.github.io/en/developer-reference#block-headers
function hasGreaterThanMedianTimestamp(newHeader, previousHeaders) {
if (previousHeaders.length < MIN_TIMESTAMP_HEADERS) return true;
const headerNormalised = utils.normalizeHeader(newHeader);
const normalizedLatestHeaders = previousHeaders.slice(
Math.max(previousHeaders.length - MIN_TIMESTAMP_HEADERS, 0),
).map(h => utils.normalizeHeader(h));
return getMedianTimestamp(normalizedLatestHeaders) < headerNormalised.time;
}

isValidBlockHeader(dgwHeaders, newHeader) {
function isValidBlockHeader(newHeader, previousHeaders, network = 'mainnet') {
if (previousHeaders.length > MIN_DGW_HEADERS) {
return newHeader.validProofOfWork()
&& newHeader.validTimestamp()
&& isValidTarget(newHeader.bits, dgwHeaders.map(h => utils.getDgwBlock(h)));
},
&& hasGreaterThanMedianTimestamp(newHeader, previousHeaders)
&& hasValidTarget(
utils.getDgwBlock(newHeader), previousHeaders.map(h => utils.getDgwBlock(h)), network,
);
}
return newHeader.validProofOfWork()
&& newHeader.validTimestamp()
&& hasGreaterThanMedianTimestamp(newHeader, previousHeaders);
}

module.exports = {
isValidBlockHeader,
};
19 changes: 0 additions & 19 deletions lib/merkleproofs.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,7 @@
const dashcore = require('@dashevo/dashcore-lib');
const calculateMnListMerkleRoot = require('./mnlistmerkleroot');

const merkleproofs = {

validateTxProofs: (merkleBlock, transactions) => merkleBlock.validMerkleTree()
&& transactions.filter(t => merkleBlock.hasTransaction(t)).length === transactions.length,

validateMnProofs(header, flags, hashes, numTransactions, cbTxHash) {
const merkleBlock = new dashcore.MerkleBlock({
header,
numTransactions,
hashes,
flags,
});

return merkleBlock.validMerkleTree() && merkleBlock.hasTransaction(cbTxHash);
},

validateMnListMerkleRoot(mnListMerkleRoot, mnList) {
return calculateMnListMerkleRoot(mnList) === mnListMerkleRoot;
},

};

module.exports = merkleproofs;
25 changes: 0 additions & 25 deletions lib/mnlistmerkleroot.js

This file was deleted.

39 changes: 36 additions & 3 deletions lib/spvchain.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,49 @@ const SpvChain = class {
init(chainType, startBlock) {
switch (chainType) {
case 'testnet':
this.network = 'testnet';
if (startBlock) {
this.root = startBlock;
break;
}
this.root = config.getTestnetGenesis();
break;
case 'devnet':
this.network = 'devnet';
if (startBlock) {
this.root = startBlock;
break;
}
this.root = config.getDevnetGenesis();
break;
case 'livenet':
this.network = 'mainnet';
if (startBlock) {
this.root = startBlock;
break;
}
this.root = config.getLivenetGenesis();
break;
case 'mainnet':
this.network = 'mainnet';
if (startBlock) {
this.root = startBlock;
break;
}
this.root = config.getLivenetGenesis();
break;
case 'lowdiff':
this.network = 'regtest';
if (startBlock) {
this.root = startBlock;
break;
}
this.root = config.getLowDiffGenesis();
break;
default:
if (startBlock) {
this.root = startBlock;
this.network = 'mainnet';
} else {
throw new Error('Unhandled chaintype or startBlock not provided');
}
Expand Down Expand Up @@ -128,9 +160,8 @@ const SpvChain = class {
addHeader(header) {
const headerNormalised = utils.normalizeHeader(header);

if (Consensus.isValidBlockHeader(this.getLongestChain(), headerNormalised)
if (Consensus.isValidBlockHeader(headerNormalised, this.getLongestChain(), this.network)
&& !this.isDuplicate(headerNormalised.hash)) {
headerNormalised.pow = utils.getDifficulty(headerNormalised.bits);
headerNormalised.children = [];
this.processValidHeader(headerNormalised);
this.setAllBranches();
Expand All @@ -142,7 +173,9 @@ const SpvChain = class {

addHeaders(headers) {
const self = this;
const allAdded = headers.reduce((acc, header) => acc && self.addHeader(header), true);
const allAdded = headers.reduce(
(acc, header) => acc && self.addHeader(header, this.network), true,
);

if (!allAdded) {
throw new Error('Some headers are invalid');
Expand Down
72 changes: 1 addition & 71 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,72 +2,6 @@
/* eslint-disable no-bitwise */
const DashUtil = require('@dashevo/dash-util');
const dashcore = require('@dashevo/dashcore-lib');
const BN = require('bn.js');

// Temp functions to be removed when EV-835 is implemented
// https://dashpay.atlassian.net/browse/EV-835
const temp = {
double256(target) {
const B192 = 0x1000000000000000000000000000000000000000000000000;
const B128 = 0x100000000000000000000000000000000;
const B64 = 0x10000000000000000;
const B0 = 0x1;

let n = 0;
let hi = null;
let lo = null;

hi = target.readUInt32LE(28, true);
lo = target.readUInt32LE(24, true);
n += ((hi * 0x100000000) + lo) * B192;

hi = target.readUInt32LE(20, true);
lo = target.readUInt32LE(16, true);
n += ((hi * 0x100000000) + lo) * B128;

hi = target.readUInt32LE(12, true);
lo = target.readUInt32LE(8, true);
n += ((hi * 0x100000000) + lo) * B64;

hi = target.readUInt32LE(4, true);
lo = target.readUInt32LE(0, true);
n += ((hi * 0x100000000) + lo) * B0;

return n;
},
fromCompact(compact) {
if (compact === 0) { return new BN(0); }

const exponent = compact >>> 24;
const negative = (compact >>> 23) & 1;

let mantissa = compact & 0x7fffff;
let num;

if (exponent <= 3) {
mantissa >>>= 8 * (3 - exponent);
num = new BN(mantissa);
} else {
num = new BN(mantissa);
num.iushln(8 * (exponent - 3));
}

if (negative) { num.ineg(); }

return num;
},
getTarget(bits) {
const target = this.fromCompact(bits);

if (target.isNeg()) { throw new Error('Target is negative.'); }

if (target.isZero()) {
throw new Error('Target is zero.');
}

return this.double256(target.toArrayLike(Buffer, 'le', 32));
},
};

module.exports = {

Expand All @@ -76,10 +10,6 @@ module.exports = {
reversedHashObj.copy(clone);
return clone.reverse().toString('hex');
},
getDifficulty(bitsInDecimal) {
const maxTargetBits = 0x1e0ffff0;
return temp.getTarget(maxTargetBits) / temp.getTarget(bitsInDecimal);
},
normalizeHeader(header) {
if (header instanceof dashcore.BlockHeader) {
return header;
Expand Down Expand Up @@ -112,7 +42,7 @@ module.exports = {
validProofOfWork(header) {
const target = DashUtil.expandTarget(header.bits);
const hash = header._getHash().reverse();
return hash.compare(target) !== 1;
return hash.compare(target) === -1;
},
createBlock(prev, bits) {
let i = 0;
Expand Down
Loading

0 comments on commit 75b421d

Please sign in to comment.