bitcore-lib-crown
Version:
A pure and powerful JavaScript Crown library.
122 lines (102 loc) • 4.36 kB
JavaScript
;
let MerkleTransaction = require('./merkletransaction');
let Hash = require('../crypto/hash');
let BufferUtil = require('../util/buffer');
let BufferReader = require('../encoding/bufferreader');
class AuxPow extends MerkleTransaction {
fromBufferReader(reader) {
super.fromBufferReader(reader);
let size = reader.readVarintNum();
this.chainMerkleBranch = []; // merkle branch of aux blockchain link
for (let i = 0; i < size; i++) {
this.chainMerkleBranch.push(reader.read(32));
}
this.chainMerkleBranchSideMask = reader.readUInt32LE(); // branch sides bitmask
// This import couldn't be at the top of file due to cyclic problem.
let BlockHeader = require('./blockheader');
this.parentBlockHeader = new BlockHeader(reader.read(80));
return this;
}
fromObject(arg) {
super.fromObject(arg);
this.chainMerkleBranch = arg.chainMerkleBranch;
this.chainMerkleBranchSideMask = arg.chainMerkleBranchSideMask;
this.parentBlockHeader = arg.parentBlockHeader;
return this;
}
toBufferWriterWhole(writer) {
super.toBufferWriterWhole(writer);
writer.writeVarintNum(this.chainMerkleBranch.length);
this.chainMerkleBranch.forEach(m => writer.write(m));
writer.writeUInt32LE(this.chainMerkleBranchSideMask);
this.parentBlockHeader.toBufferWriter(writer);
return writer;
}
check(blockHash, chainId) {
if (this.merkleBranchSideMask !== 0) throw new Error("AuxPow is not a generate.");
if (this.parentBlockHeader.versionObject.getChainId() === chainId) {
throw new Error("Parent block's chain ID is the same as ours.");
}
let chainRootHash = BufferUtil.reverse(AuxPow._computeMerkleBranch(
blockHash,
this.chainMerkleBranch,
this.chainMerkleBranchSideMask
));
let scriptSig = this.inputs[0]._scriptBuffer;
let symbolPos = scriptSig.indexOf(AuxPow.SCRIPT_SYMBOL);
let hashPos = scriptSig.indexOf(chainRootHash);
if (!BufferUtil.equals(
AuxPow._computeMerkleBranch(this._getHash(), this.merkleBranch, this.merkleBranchSideMask), this.parentBlockHeader.merkleRoot
)) throw new Error("Merkle root incorrect.");
if (hashPos === -1) throw new Error("Chain merkle root not found in parent block coinbase scriptSig.");
if (symbolPos !== -1) {
if (scriptSig.indexOf(AuxPow.SCRIPT_SYMBOL, symbolPos + 1) !== -1) throw new Error(
`Multiple "${AuxPow.SCRIPT_SYMBOL.toString("hex")}" symbols in parent block coinbase scriptSig.`
);
if (symbolPos + AuxPow.SCRIPT_SYMBOL.length !== hashPos) throw new Error(
`"${AuxPow.SCRIPT_SYMBOL.toString("hex")}" symbol is not just before chain merkle root.`
);
}
else {
if (hashPos > 20) throw new Error(
"Chain merkle root must start in the first 20 bytes of parent block coinbase scriptSig."
);
}
let sizePos = hashPos + chainRootHash.length;
if (scriptSig.length - sizePos < 8) throw new Error(
"Chain merkle tree size and nonce not found in parent block coinbase scriptSig."
);
let size = new BufferReader(scriptSig.slice(sizePos, sizePos + 4)).readUInt32LE();
let merkleHeight = this.chainMerkleBranch.length;
if (size !== 1 << merkleHeight) throw new Error(
"Merkle branch size does not match parent block coinbase scriptSig."
);
let nonce = new BufferReader(scriptSig.slice(sizePos + 4, sizePos + 8)).readUInt32LE();
if (
this.chainMerkleBranchSideMask !==
AuxPow.getExpectedChainMerkleBranchSideMask(nonce, chainId, merkleHeight)
) throw new Error("Wrong chain merkle branch side mask.");
}
static _computeMerkleBranch(hash, merkleBranch, sideMask) {
if (merkleBranch.length > 30) throw new Error("Merkle branch too long.");
merkleBranch.forEach(item => {
if (sideMask & 1) {
hash = Hash.sha256sha256(Buffer.concat([item, hash]));
}
else {
hash = Hash.sha256sha256(Buffer.concat([hash, item]));
}
sideMask >>= 1;
});
return hash;
}
static getExpectedChainMerkleBranchSideMask(nonce, chainId, h) {
let rand = nonce;
rand = rand * 1103515245 + 12345;
rand += chainId;
rand = rand * 1103515245 + 12345;
return rand % (1 << h);
}
}
AuxPow.SCRIPT_SYMBOL = Buffer.from("fabe6d6d","hex");
module.exports = AuxPow;