Draft implementation of Ravencoin utils
This commit is contained in:
85
index.js
85
index.js
@@ -3,6 +3,9 @@ module.exports = require('bindings')('cryptoforknote.node');
|
|||||||
const SHA3 = require('sha3');
|
const SHA3 = require('sha3');
|
||||||
const bignum = require('bignum');
|
const bignum = require('bignum');
|
||||||
const bitcoin = require('bitcoinjs-lib');
|
const bitcoin = require('bitcoinjs-lib');
|
||||||
|
const varuint = require('varuint-bitcoin');
|
||||||
|
const crypto = require('crypt');
|
||||||
|
const fastMerkleRoot = require('merkle-lib/fastRoot');
|
||||||
const promise = require('promise');
|
const promise = require('promise');
|
||||||
const merklebitcoin = promise.denodeify(require('merkle-bitcoin'));
|
const merklebitcoin = promise.denodeify(require('merkle-bitcoin'));
|
||||||
|
|
||||||
@@ -25,43 +28,38 @@ function reverseBuffer(buff) {
|
|||||||
function getMerkleRoot(rpcData, generateTxRaw) {
|
function getMerkleRoot(rpcData, generateTxRaw) {
|
||||||
hashes = [ reverseBuffer(new Buffer(generateTxRaw, 'hex')).toString('hex') ];
|
hashes = [ reverseBuffer(new Buffer(generateTxRaw, 'hex')).toString('hex') ];
|
||||||
rpcData.transactions.forEach(function (value) {
|
rpcData.transactions.forEach(function (value) {
|
||||||
if (value.txid !== undefined) {
|
hashes.push(value.txid ? value.txid : value.hash);
|
||||||
hashes.push(value.txid);
|
|
||||||
} else {
|
|
||||||
hashes.push(value.hash);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
if (hashes.length === 1) return hashes[0];
|
if (hashes.length === 1) return hashes[0];
|
||||||
return Object.values(merklebitcoin(hashes))[2].root;
|
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;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
function txesHaveWitnessCommit(transactions) {
|
||||||
|
return (
|
||||||
|
transactions instanceof Array &&
|
||||||
|
transactions[0] &&
|
||||||
|
transactions[0].ins &&
|
||||||
|
transactions[0].ins instanceof Array &&
|
||||||
|
transactions[0].ins[0] &&
|
||||||
|
transactions[0].ins[0].witness &&
|
||||||
|
transactions[0].ins[0].witness instanceof Array &&
|
||||||
|
transactions[0].ins[0].witness.length > 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMerkleRoot2(transactions) {
|
||||||
|
if (transactions.length === 0) return null;
|
||||||
|
const forWitness = txesHaveWitnessCommit(transactions);
|
||||||
|
const hashes = transactions.map(transaction => transaction.getHash(forWitness));
|
||||||
|
const hash256 = function (buffer) { return crypto.createHash('sha256').update(buffer).digest(); };
|
||||||
|
const rootHash = fastMerkleRoot(hashes, hash256);
|
||||||
|
return forWitness ? hash256(Buffer.concat([rootHash, transactions[0].ins[0].witness[0]])) : rootHash;
|
||||||
|
}
|
||||||
|
|
||||||
let last_epoch_number;
|
let last_epoch_number;
|
||||||
let last_seedhash;
|
let last_seed_hash;
|
||||||
|
|
||||||
module.exports.RavenBlockTemplate = function(rpcData, poolAddress) {
|
module.exports.RavenBlockTemplate = function(rpcData, poolAddress) {
|
||||||
// epoch length
|
|
||||||
const EPOCH_LENGTH = 7500;
|
|
||||||
const poolAddrHash = bitcoin.address.fromBase58Check(poolAddress).hash;
|
const poolAddrHash = bitcoin.address.fromBase58Check(poolAddress).hash;
|
||||||
let txCoinbase = new bitcoin.Transaction();
|
let txCoinbase = new bitcoin.Transaction();
|
||||||
{ // input for coinbase tx
|
{ // input for coinbase tx
|
||||||
@@ -78,6 +76,7 @@ module.exports.RavenBlockTemplate = function(rpcData, poolAddress) {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
txCoinbase.addInput(
|
txCoinbase.addInput(
|
||||||
|
// will be used for our reserved_offset extra_nonce
|
||||||
new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex'),
|
new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex'),
|
||||||
0xFFFFFFFF, 0xFFFFFFFF,
|
0xFFFFFFFF, 0xFFFFFFFF,
|
||||||
new Buffer.concat([serializedBlockHeight, Buffer('6b6177706f77', 'hex')])
|
new Buffer.concat([serializedBlockHeight, Buffer('6b6177706f77', 'hex')])
|
||||||
@@ -85,7 +84,7 @@ module.exports.RavenBlockTemplate = function(rpcData, poolAddress) {
|
|||||||
|
|
||||||
txCoinbase.addOutput(scriptCompile(poolAddrHash), Math.floor(rpcData.coinbasevalue));
|
txCoinbase.addOutput(scriptCompile(poolAddrHash), Math.floor(rpcData.coinbasevalue));
|
||||||
|
|
||||||
if (rpcData.default_witness_commitment !== undefined) {
|
if (rpcData.default_witness_commitment) {
|
||||||
txCoinbase.addOutput(new Buffer(rpcData.default_witness_commitment, 'hex'), 0);
|
txCoinbase.addOutput(new Buffer(rpcData.default_witness_commitment, 'hex'), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,7 +105,7 @@ module.exports.RavenBlockTemplate = function(rpcData, poolAddress) {
|
|||||||
header, // 80 bytes
|
header, // 80 bytes
|
||||||
new Buffer('EEEEEEEEEEEEEEEE', 'hex'), // 8 bytes
|
new Buffer('EEEEEEEEEEEEEEEE', 'hex'), // 8 bytes
|
||||||
new Buffer('EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE', 'hex'), // 32 bytes
|
new Buffer('EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE', 'hex'), // 32 bytes
|
||||||
varIntBuffer(rpcData.transactions.length + 1)
|
varuint.encode(rpcData.transactions.length + 1, new Buffer(), 0)
|
||||||
]);
|
]);
|
||||||
const offset1 = blob.length;
|
const offset1 = blob.length;
|
||||||
blob = new Buffer.concat([ blob, new Buffer(txCoinbase.toHex(), 'hex') ]);
|
blob = new Buffer.concat([ blob, new Buffer(txCoinbase.toHex(), 'hex') ]);
|
||||||
@@ -115,15 +114,16 @@ module.exports.RavenBlockTemplate = function(rpcData, poolAddress) {
|
|||||||
blob = new Buffer.concat([ blob, new Buffer(value.data, 'hex') ]);
|
blob = new Buffer.concat([ blob, new Buffer(value.data, 'hex') ]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const EPOCH_LENGTH = 7500;
|
||||||
const epoch_number = Math.floor(rpcData.height / EPOCH_LENGTH);
|
const epoch_number = Math.floor(rpcData.height / EPOCH_LENGTH);
|
||||||
if (last_epoch_number !== epoch_number) {
|
if (last_epoch_number !== epoch_number) {
|
||||||
let sha3 = new SHA3.SHA3Hash(256);
|
let sha3 = new SHA3.SHA3Hash(256);
|
||||||
if (last_epoch_number && last_epoch_number + 1 === epoch_number) {
|
if (last_epoch_number && last_epoch_number + 1 === epoch_number) {
|
||||||
last_seedhash = sha3.update(last_seedhash).digest();
|
last_seed_hash = sha3.update(last_seed_hash).digest();
|
||||||
} else {
|
} else {
|
||||||
last_seedhash = new Buffer(32, 0);
|
last_seed_hash = new Buffer(32, 0);
|
||||||
for (let i = 0; i < epoch_number; i++) {
|
for (let i = 0; i < epoch_number; i++) {
|
||||||
last_seedhash = sha3.update(last_seedhash).digest();
|
last_seed_hash = sha3.update(last_seed_hash).digest();
|
||||||
sha3.reset();
|
sha3.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,9 +136,24 @@ module.exports.RavenBlockTemplate = function(rpcData, poolAddress) {
|
|||||||
return {
|
return {
|
||||||
blocktemplate_blob: blob.toString('hex'),
|
blocktemplate_blob: blob.toString('hex'),
|
||||||
reserved_offset: offset1 + 4 /* txCoinbase.version */ + 1 /* txCoinbase.vinLen */,
|
reserved_offset: offset1 + 4 /* txCoinbase.version */ + 1 /* txCoinbase.vinLen */,
|
||||||
seed_hash: last_seedhash.toString('hex'),
|
seed_hash: last_seed_hash.toString('hex'),
|
||||||
difficulty: difficulty,
|
difficulty: difficulty,
|
||||||
height: rpcData.height,
|
height: rpcData.height,
|
||||||
rpc: rpcData,
|
rpc: rpcData,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports.convertRavenBlob = function(blobBuffer) {
|
||||||
|
let header = blobBuffer.slice(0, 80);
|
||||||
|
let offset = 80 + 8 + 32;
|
||||||
|
const nTransactions = varuint.decode(blobBuffer, offset);
|
||||||
|
offset += varuint.decode.bytes;
|
||||||
|
let transactions = [];
|
||||||
|
for (let i = 0; i < nTransactions; ++i) {
|
||||||
|
const tx = bitcoin.Transaction.fromBuffer(blobBuffer.slice(offset), true);
|
||||||
|
transactions.push(tx);
|
||||||
|
offset += tx.byteLength();
|
||||||
|
}
|
||||||
|
getMerkleRoot2(transactions).copy(header, 4 + 32);
|
||||||
|
return header;
|
||||||
|
};
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
"nan": "^2.0.0",
|
"nan": "^2.0.0",
|
||||||
"bignum": "^0.13.1",
|
"bignum": "^0.13.1",
|
||||||
"sha3": "*",
|
"sha3": "*",
|
||||||
|
"varuint-bitcoin": "^1.0.4",
|
||||||
"bitcoinjs-lib": "git+https://github.com/bitcoinjs/bitcoinjs-lib.git#533d6c2e6d0aa4111f7948b1c12003cf6ef83137",
|
"bitcoinjs-lib": "git+https://github.com/bitcoinjs/bitcoinjs-lib.git#533d6c2e6d0aa4111f7948b1c12003cf6ef83137",
|
||||||
"merkle-bitcoin": "git+https://github.com/joshuayabut/merkle-bitcoin.git#ec00ae20ba60d444e150ead03c747695ddaa83a1"
|
"merkle-bitcoin": "git+https://github.com/joshuayabut/merkle-bitcoin.git#ec00ae20ba60d444e150ead03c747695ddaa83a1"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user