From e8b9ff4a9f2ea31d7822833dc21c86bba813942c Mon Sep 17 00:00:00 2001 From: MoneroOcean Date: Fri, 30 Oct 2020 22:42:21 +0000 Subject: [PATCH] Draft implementation of Ravencoin utils --- index.js | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 133 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 1642e66..68ab1d8 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,135 @@ -module.exports = function () { - let obj = require('bindings')('cryptoforknote.node')(); - return obj; +module.exports = require('bindings')('cryptoforknote.node'); + +const SHA3 = require('sha3'); +const bignum = require('bignum'); +const bitcoin = require('bitcoinjs-lib'); +const promise = require('promise'); +const merklebitcoin = promise.denodeify(require('merkle-bitcoin')); + +function scriptCompile(addrHash) { + return bitcoin.script.compile([ + bitcoin.opcodes.OP_DUP, + bitcoin.opcodes.OP_HASH160, + addrHash, + bitcoin.opcodes.OP_EQUALVERIFY, + bitcoin.opcodes.OP_CHECKSIG + ]); +} + +function reverseBuffer(buff) { + let reversed = new Buffer(buff.length); + for (var i = buff.length - 1; i >= 0; i--) reversed[buff.length - i - 1] = buff[i]; + return reversed; +} + +function getMerkleRoot(rpcData, generateTxRaw) { + hashes = [ reverseBuffer(new Buffer(generateTxRaw, 'hex')).toString('hex') ]; + rpcData.transactions.forEach(function (value) { + if (value.txid !== undefined) { + hashes.push(value.txid); + } else { + hashes.push(value.hash); + } + }); + if (hashes.length === 1) return hashes[0]; + return Object.values(merklebitcoin(hashes))[2].root; }; +function varIntBuffer(n) { + if (n < 0xfd) { + return new Buffer([n]); + } else if (n <= 0xffff) { + let buff = new Buffer(3); + buff[0] = 0xfd; + buff.writeUInt16LE(n, 1); + return buff; + } else if (n <= 0xffffffff) { + let buff = new Buffer(5); + buff[0] = 0xfe; + buff.writeUInt32LE(n, 1); + return buff; + } else { + let buff = new Buffer(9); + buff[0] = 0xff; + buff.writeUInt64LE(n, 1); + return buff; + } +}; + +module.exports.RavenBlockTemplate = function(rpcData, poolAddress) { + // epoch length + const EPOCH_LENGTH = 7500; + const poolAddrHash = bitcoin.address.fromBase58Check(poolAddress).hash; + let txCoinbase = new bitcoin.Transaction(); + { // input for coinbase tx + let blockHeightSerial = rpcData.height.toString(16).length % 2 === 0 ? + rpcData.height.toString(16) : + '0' + rpcData.height.toString(16); + const bytesHeight = Math.ceil((rpcData.height << 1).toString(2).length / 8); + const lengthDiff = blockHeightSerial.length/2 - bytesHeight; + for (let i = 0; i < lengthDiff; i++) blockHeightSerial = blockHeightSerial + '00'; + const serializedBlockHeight = new Buffer.concat([ + new Buffer('0' + bytesHeight, 'hex'), + reverseBuffer(new Buffer(blockHeightSerial, 'hex')), + new Buffer('00', 'hex') // OP_0 + ]); + + txCoinbase.addInput( + new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex'), + 0xFFFFFFFF, 0xFFFFFFFF, + new Buffer.concat([serializedBlockHeight, Buffer('6b6177706f77', 'hex')]) + ); + + txCoinbase.addOutput(scriptCompile(poolAddrHash), Math.floor(rpcData.coinbasevalue)); + + if (rpcData.default_witness_commitment !== undefined) { + txCoinbase.addOutput(new Buffer(rpcData.default_witness_commitment, 'hex'), 0); + } + } + const merkleRoot = getMerkleRoot(rpcData, txCoinbase.getHash().toString('hex')); + + let header = new Buffer(80); + { let position = 0; + header.writeUInt32BE(rpcData.height, position, 4); // height 42-46 + header.write(rpcData.bits, position += 4, 4, 'hex'); // bits 47-50 + header.writeUInt32BE(rpcData.curtime, position += 4, 4, 'hex'); // nTime 51-54 + header.write(merkleRoot, position += 4, 32, 'hex'); // merkelRoot 55-87 + header.write(rpcData.previousblockhash, position += 32, 32, 'hex'); // prevblockhash 88-120 + header.writeUInt32BE(rpcData.version, position += 32, 4); // version 121-153 + header = reverseBuffer(header); + } + + let blob = new Buffer.concat([ + header, // 80 bytes + new Buffer('EEEEEEEEEEEEEEEE', 'hex'), // 8 bytes + new Buffer('EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE', 'hex'), // 32 bytes + varIntBuffer(rpcData.transactions.length + 1) + ]); + const offset1 = blob.length; + blob = new Buffer.concat([ blob, new Buffer(txCoinbase.toHex(), 'hex') ]); + + rpcData.transactions.forEach(function (value) { + blob = new Buffer.concat([ blob, new Buffer(value.data, 'hex') ]); + }); + + let sha3 = new SHA3.SHA3Hash(256); + let seedhash_buf = new Buffer(32, 0); + const epoch_number = Math.floor(rpcData.height / EPOCH_LENGTH); + for (let i=0; i < epoch_number; i++) { + seedhash_buf = sha3.update(seedhash_buf).digest(); + sha3.reset(); + } + const seed_hash = seedhash_buf.toString('hex'); + + const diff1 = 0x00000000ff000000000000000000000000000000000000000000000000000000; + const difficulty = parseFloat((diff1 / bignum(rpcData.target, 16).toNumber()).toFixed(9)) + + return { + blocktemplate_blob: blob, + reserved_offset: offset1 + 4 /* txCoinbase.version */ + 1 /* txCoinbase.marker */ + 1 /* txCoinbase.flag */ + 1 /* txCoinbase.vinLen */, + seed_hash: seed_hash, + difficulty: difficulty, + height: rpcData.height, + rpc: rpcData, + }; +};