Add Monero and Peya block adapters to shared util
This commit is contained in:
55
index.js
55
index.js
@@ -1,15 +1,46 @@
|
||||
module.exports = require('bindings')('cryptoforknote.node');
|
||||
|
||||
const SHA3 = require('sha3');
|
||||
const bignum = require('bignum');
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
const varuint = require('varuint-bitcoin');
|
||||
const crypto = require('crypto');
|
||||
const fastMerkleRoot = require('merkle-lib/fastRoot');
|
||||
|
||||
const rtm = require('cryptoforknote-util/rtm');
|
||||
let bitcoin;
|
||||
let bignum;
|
||||
let SHA3;
|
||||
let varuint;
|
||||
let fastMerkleRoot;
|
||||
let rtm;
|
||||
|
||||
function getBitcoin() {
|
||||
if (!bitcoin) bitcoin = require('bitcoinjs-lib');
|
||||
return bitcoin;
|
||||
}
|
||||
|
||||
function getBignum() {
|
||||
if (!bignum) bignum = require('bignum');
|
||||
return bignum;
|
||||
}
|
||||
|
||||
function getSHA3() {
|
||||
if (!SHA3) SHA3 = require('sha3');
|
||||
return SHA3;
|
||||
}
|
||||
|
||||
function getVaruint() {
|
||||
if (!varuint) varuint = require('varuint-bitcoin');
|
||||
return varuint;
|
||||
}
|
||||
|
||||
function getFastMerkleRoot() {
|
||||
if (!fastMerkleRoot) fastMerkleRoot = require('merkle-lib/fastRoot');
|
||||
return fastMerkleRoot;
|
||||
}
|
||||
|
||||
function getRtm() {
|
||||
if (!rtm) rtm = require('cryptoforknote-util/rtm');
|
||||
return rtm;
|
||||
}
|
||||
|
||||
function scriptCompile(addrHash) {
|
||||
const bitcoin = getBitcoin();
|
||||
return bitcoin.script.compile([
|
||||
bitcoin.opcodes.OP_DUP,
|
||||
bitcoin.opcodes.OP_HASH160,
|
||||
@@ -65,6 +96,7 @@ function transaction_hash3(transaction, forWitness) {
|
||||
}
|
||||
|
||||
function getMerkleRoot(transactions, transaction_hash_func, detectWitness) {
|
||||
const fastMerkleRoot = getFastMerkleRoot();
|
||||
if (transactions.length === 0) return Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex')
|
||||
const forWitness = detectWitness ? txesHaveWitnessCommit(transactions) : false;
|
||||
const hashes = transactions.map(transaction => transaction_hash_func(transaction, forWitness));
|
||||
@@ -76,6 +108,7 @@ let last_epoch_number;
|
||||
let last_seed_hash;
|
||||
|
||||
module.exports.baseDiff = function() {
|
||||
const bignum = getBignum();
|
||||
return bignum('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 16);
|
||||
};
|
||||
|
||||
@@ -84,6 +117,9 @@ module.exports.baseRavenDiff = function() {
|
||||
};
|
||||
|
||||
module.exports.RavenBlockTemplate = function(rpcData, poolAddress) {
|
||||
const bignum = getBignum();
|
||||
const bitcoin = getBitcoin();
|
||||
const varuint = getVaruint();
|
||||
const poolAddrHash = bitcoin.address.fromBase58Check(poolAddress).hash;
|
||||
|
||||
let txCoinbase = new bitcoin.Transaction();
|
||||
@@ -147,6 +183,7 @@ module.exports.RavenBlockTemplate = function(rpcData, poolAddress) {
|
||||
const EPOCH_LENGTH = 7500;
|
||||
const epoch_number = Math.floor(rpcData.height / EPOCH_LENGTH);
|
||||
if (last_epoch_number !== epoch_number) {
|
||||
const SHA3 = getSHA3();
|
||||
let sha3 = new SHA3.SHA3Hash(256);
|
||||
if (last_epoch_number && last_epoch_number + 1 === epoch_number) {
|
||||
last_seed_hash = sha3.update(last_seed_hash).digest();
|
||||
@@ -176,6 +213,8 @@ module.exports.RavenBlockTemplate = function(rpcData, poolAddress) {
|
||||
};
|
||||
|
||||
function update_merkle_root_hash(offset, payload, blob_in, blob_out, transaction_hash_func, detectWitness) {
|
||||
const bitcoin = getBitcoin();
|
||||
const varuint = getVaruint();
|
||||
const nTransactions = varuint.decode(blob_in, offset);
|
||||
offset += varuint.decode.bytes;
|
||||
let transactions = [];
|
||||
@@ -214,6 +253,7 @@ module.exports.constructNewDeroBlob = function(blockTemplate, nonceBuff) {
|
||||
};
|
||||
|
||||
module.exports.EthBlockTemplate = function(rpcData) {
|
||||
const bignum = getBignum();
|
||||
const difficulty = module.exports.baseDiff().div(bignum(rpcData[2].substr(2), 16)).toNumber();
|
||||
return {
|
||||
hash: rpcData[0].substr(2),
|
||||
@@ -224,6 +264,7 @@ module.exports.EthBlockTemplate = function(rpcData) {
|
||||
};
|
||||
|
||||
module.exports.ErgBlockTemplate = function(rpcData) {
|
||||
const bignum = getBignum();
|
||||
const difficulty = module.exports.baseDiff().div(bignum(rpcData.b)).toNumber();
|
||||
return {
|
||||
hash: rpcData.msg,
|
||||
@@ -234,7 +275,7 @@ module.exports.ErgBlockTemplate = function(rpcData) {
|
||||
};
|
||||
|
||||
module.exports.RtmBlockTemplate = function(rpcData, poolAddress) {
|
||||
return rtm.RtmBlockTemplate(rpcData, poolAddress);
|
||||
return getRtm().RtmBlockTemplate(rpcData, poolAddress);
|
||||
};
|
||||
|
||||
module.exports.convertRtmBlob = function(blobBuffer) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
#include <boost/optional/optional.hpp>
|
||||
#include <boost/functional/hash/hash.hpp>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
@@ -30,6 +31,16 @@
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
inline bool is_salvium_family_blob_type(const BLOB_TYPE blob_type)
|
||||
{
|
||||
return blob_type == BLOB_TYPE_CRYPTONOTE_SALVIUM;
|
||||
}
|
||||
|
||||
inline bool is_monero_family_blob_type(const BLOB_TYPE blob_type)
|
||||
{
|
||||
return blob_type == BLOB_TYPE_CRYPTONOTE;
|
||||
}
|
||||
|
||||
struct block;
|
||||
class transaction;
|
||||
class transaction_prefix;
|
||||
@@ -174,6 +185,7 @@ namespace cryptonote
|
||||
};
|
||||
|
||||
typedef boost::variant<txin_gen, txin_to_script, txin_to_scripthash, txin_to_key> txin_v;
|
||||
typedef boost::variant<txin_gen, txin_to_script, txin_to_scripthash, txin_to_key> monero_txin_v;
|
||||
|
||||
typedef boost::variant<txout_to_script, txout_to_scripthash, txout_to_key, txout_to_tagged_key, txout_to_carrot_v1> txout_target_v;
|
||||
|
||||
@@ -188,6 +200,43 @@ namespace cryptonote
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct monero_txout_to_key
|
||||
{
|
||||
monero_txout_to_key() { }
|
||||
explicit monero_txout_to_key(const crypto::public_key &_key) : key(_key) { }
|
||||
crypto::public_key key;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(key)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct monero_txout_to_tagged_key
|
||||
{
|
||||
monero_txout_to_tagged_key() { }
|
||||
monero_txout_to_tagged_key(const crypto::public_key &_key, const crypto::view_tag &_view_tag) : key(_key), view_tag(_view_tag) { }
|
||||
crypto::public_key key;
|
||||
crypto::view_tag view_tag;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(key)
|
||||
FIELD(view_tag)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
typedef boost::variant<txout_to_script, txout_to_scripthash, monero_txout_to_key, monero_txout_to_tagged_key> monero_txout_target_v;
|
||||
|
||||
struct monero_tx_out
|
||||
{
|
||||
uint64_t amount;
|
||||
monero_txout_target_v target;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
VARINT_FIELD(amount)
|
||||
FIELD(target)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
class protocol_tx_data_t {
|
||||
public:
|
||||
uint8_t version;
|
||||
@@ -205,6 +254,71 @@ namespace cryptonote
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
class monero_coinbase_transaction_t
|
||||
{
|
||||
public:
|
||||
size_t version;
|
||||
uint64_t unlock_time;
|
||||
|
||||
std::vector<monero_txin_v> vin;
|
||||
std::vector<monero_tx_out> vout;
|
||||
std::vector<uint8_t> extra;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
VARINT_FIELD(version)
|
||||
if (version != 2) return false;
|
||||
VARINT_FIELD(unlock_time)
|
||||
FIELD(vin)
|
||||
FIELD(vout)
|
||||
FIELD(extra)
|
||||
uint8_t rct_signatures_type = 0;
|
||||
FIELD(rct_signatures_type)
|
||||
if (rct_signatures_type != 0) return false;
|
||||
END_SERIALIZE()
|
||||
|
||||
public:
|
||||
monero_coinbase_transaction_t() { set_null(); }
|
||||
void set_null()
|
||||
{
|
||||
version = 2;
|
||||
unlock_time = 0;
|
||||
vin.clear();
|
||||
vout.clear();
|
||||
extra.clear();
|
||||
}
|
||||
};
|
||||
|
||||
struct monero_block_header_t
|
||||
{
|
||||
uint8_t major_version;
|
||||
uint8_t minor_version;
|
||||
uint64_t timestamp;
|
||||
crypto::hash prev_id;
|
||||
uint32_t nonce;
|
||||
|
||||
monero_block_header_t(): major_version(0), minor_version(0), timestamp(0), prev_id(cryptonote::null_hash), nonce(0) {}
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
VARINT_FIELD(major_version)
|
||||
VARINT_FIELD(minor_version)
|
||||
VARINT_FIELD(timestamp)
|
||||
FIELD(prev_id)
|
||||
FIELD(nonce)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct monero_block_t: public monero_block_header_t
|
||||
{
|
||||
monero_coinbase_transaction_t miner_tx;
|
||||
std::vector<crypto::hash> tx_hashes;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELDS(*static_cast<monero_block_header_t *>(this))
|
||||
FIELD(miner_tx)
|
||||
FIELD(tx_hashes)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct erc_token_t
|
||||
{
|
||||
uint8_t version;
|
||||
@@ -537,19 +651,46 @@ namespace cryptonote
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct aux_header_t
|
||||
{
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct block: public block_header
|
||||
{
|
||||
transaction miner_tx;
|
||||
transaction protocol_tx;
|
||||
std::vector<crypto::hash> tx_hashes;
|
||||
boost::optional<aux_header_t> aux_header;
|
||||
|
||||
void set_blob_type(enum BLOB_TYPE bt) { miner_tx.blob_type = protocol_tx.blob_type = blob_type = bt; }
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELDS(*static_cast<block_header *>(this))
|
||||
FIELD(miner_tx)
|
||||
FIELD(protocol_tx)
|
||||
if (is_salvium_family_blob_type(blob_type))
|
||||
FIELD(protocol_tx)
|
||||
FIELD(tx_hashes)
|
||||
if (is_salvium_family_blob_type(blob_type) && major_version >= HF_VERSION_MERGE_MINING)
|
||||
{
|
||||
uint8_t has_aux_header = !!aux_header;
|
||||
FIELD(has_aux_header)
|
||||
if (has_aux_header)
|
||||
{
|
||||
if (!typename Archive<W>::is_saving())
|
||||
aux_header = aux_header_t();
|
||||
FIELD_N("aux_header", *aux_header)
|
||||
}
|
||||
else if (!typename Archive<W>::is_saving())
|
||||
{
|
||||
aux_header = boost::none;
|
||||
}
|
||||
}
|
||||
else if (!typename Archive<W>::is_saving())
|
||||
{
|
||||
aux_header = boost::none;
|
||||
}
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
@@ -615,6 +756,8 @@ VARIANT_TAG(binary_archive, cryptonote::txout_to_scripthash, 0x1);
|
||||
VARIANT_TAG(binary_archive, cryptonote::txout_to_key, 0x2);
|
||||
VARIANT_TAG(binary_archive, cryptonote::txout_to_tagged_key, 0x3);
|
||||
VARIANT_TAG(binary_archive, cryptonote::txout_to_carrot_v1, 0x4);
|
||||
VARIANT_TAG(binary_archive, cryptonote::monero_txout_to_key, 0x2);
|
||||
VARIANT_TAG(binary_archive, cryptonote::monero_txout_to_tagged_key, 0x3);
|
||||
VARIANT_TAG(binary_archive, cryptonote::protocol_tx_data_t, 0x0);
|
||||
VARIANT_TAG(binary_archive, cryptonote::token_metadata_t, 0x0);
|
||||
VARIANT_TAG(binary_archive, cryptonote::erc_token_t, 0x0);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#define CURRENT_TRANSACTION_VERSION 5
|
||||
#define HF_VERSION_ENABLE_N_OUTS 2
|
||||
#define HF_VERSION_MERGE_MINING 13
|
||||
#define TRANSACTION_VERSION_N_OUTS 3
|
||||
#define TRANSACTION_VERSION_CARROT 4
|
||||
#define TRANSACTION_VERSION_ENABLE_TOKENS 5
|
||||
|
||||
97
src/main.cc
97
src/main.cc
@@ -20,6 +20,56 @@ using namespace cryptonote;
|
||||
// cryptonote::append_mm_tag_to_extra writes byte with TX_EXTRA_MERGE_MINING_TAG (1 here) and VARINT DEPTH (2 here)
|
||||
const size_t MM_NONCE_SIZE = 1 + 2 + sizeof(crypto::hash);
|
||||
|
||||
static bool parse_monero_block_from_blob(const blobdata &input, cryptonote::monero_block_t &b) {
|
||||
std::stringstream ss;
|
||||
ss << input;
|
||||
binary_archive<false> ba(ss);
|
||||
return ::serialization::serialize(ba, b);
|
||||
}
|
||||
|
||||
static bool monero_block_to_blob(const cryptonote::monero_block_t &b, blobdata &blob) {
|
||||
return cryptonote::t_serializable_object_to_blob(b, blob);
|
||||
}
|
||||
|
||||
static crypto::hash get_monero_coinbase_hash(const cryptonote::monero_coinbase_transaction_t &t) {
|
||||
crypto::hash hashes[3];
|
||||
|
||||
std::ostringstream s;
|
||||
binary_archive<true> a(s);
|
||||
::serialization::serialize(a, const_cast<cryptonote::monero_coinbase_transaction_t&>(t));
|
||||
if (s.str().empty()) {
|
||||
return cryptonote::null_hash;
|
||||
}
|
||||
|
||||
crypto::cn_fast_hash(s.str().data(), s.str().size() - 1, hashes[0]);
|
||||
const blobdata blob = blobdata(1, '\0');
|
||||
cryptonote::get_blob_hash(blob, hashes[1]);
|
||||
hashes[2] = cryptonote::null_hash;
|
||||
return crypto::cn_fast_hash(hashes, sizeof(hashes));
|
||||
}
|
||||
|
||||
static bool get_monero_block_hashing_blob(const cryptonote::monero_block_t &b, blobdata &blob) {
|
||||
blob = cryptonote::t_serializable_object_to_blob(static_cast<const cryptonote::monero_block_header_t&>(b));
|
||||
std::vector<crypto::hash> tx_ids;
|
||||
tx_ids.reserve(b.tx_hashes.size() + 1);
|
||||
tx_ids.push_back(get_monero_coinbase_hash(b.miner_tx));
|
||||
tx_ids.insert(tx_ids.end(), b.tx_hashes.begin(), b.tx_hashes.end());
|
||||
crypto::hash tree_root_hash = cryptonote::null_hash;
|
||||
cryptonote::get_tx_tree_hash(tx_ids, tree_root_hash);
|
||||
blob.append(reinterpret_cast<const char*>(&tree_root_hash), sizeof(tree_root_hash));
|
||||
blob.append(tools::get_varint_data(b.tx_hashes.size() + 1));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool get_monero_block_hash(const cryptonote::monero_block_t &b, crypto::hash &res) {
|
||||
blobdata blob;
|
||||
if (!monero_block_to_blob(b, blob)) {
|
||||
return false;
|
||||
}
|
||||
cryptonote::get_blob_hash(blob, res);
|
||||
return true;
|
||||
}
|
||||
|
||||
blobdata uint64be_to_blob(uint64_t num) {
|
||||
blobdata res = " ";
|
||||
res[0] = num >> 56 & 0xff;
|
||||
@@ -149,10 +199,16 @@ NAN_METHOD(convert_blob) { // (parentBlockBuffer, cnBlobType)
|
||||
blob_type = static_cast<enum BLOB_TYPE>(Nan::To<int>(info[1]).FromMaybe(0));
|
||||
}
|
||||
|
||||
block b = AUTO_VAL_INIT(b);
|
||||
b.set_blob_type(blob_type);
|
||||
if (!parse_and_validate_block_from_blob(input, b)) return THROW_ERROR_EXCEPTION("Failed to parse block 2");
|
||||
if (!get_block_hashing_blob(b, output)) return THROW_ERROR_EXCEPTION("convert_blob: Failed to create mining block");
|
||||
if (blob_type == BLOB_TYPE_CRYPTONOTE) {
|
||||
monero_block_t b = AUTO_VAL_INIT(b);
|
||||
if (!parse_monero_block_from_blob(input, b)) return THROW_ERROR_EXCEPTION("Failed to parse block 2");
|
||||
if (!get_monero_block_hashing_blob(b, output)) return THROW_ERROR_EXCEPTION("convert_blob: Failed to create mining block");
|
||||
} else {
|
||||
block b = AUTO_VAL_INIT(b);
|
||||
b.set_blob_type(blob_type);
|
||||
if (!parse_and_validate_block_from_blob(input, b)) return THROW_ERROR_EXCEPTION("Failed to parse block 2");
|
||||
if (!get_block_hashing_blob(b, output)) return THROW_ERROR_EXCEPTION("convert_blob: Failed to create mining block");
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> returnValue = Nan::CopyBuffer((char*)output.data(), output.size()).ToLocalChecked();
|
||||
info.GetReturnValue().Set(returnValue);
|
||||
@@ -173,12 +229,17 @@ NAN_METHOD(get_block_id) {
|
||||
blob_type = static_cast<enum BLOB_TYPE>(Nan::To<int>(info[1]).FromMaybe(0));
|
||||
}
|
||||
|
||||
block b = AUTO_VAL_INIT(b);
|
||||
b.set_blob_type(blob_type);
|
||||
if (!parse_and_validate_block_from_blob(input, b)) return THROW_ERROR_EXCEPTION("Failed to parse block");
|
||||
|
||||
crypto::hash block_id;
|
||||
if (!get_block_hash(b, block_id)) return THROW_ERROR_EXCEPTION("Failed to calculate hash for block");
|
||||
if (blob_type == BLOB_TYPE_CRYPTONOTE) {
|
||||
monero_block_t b = AUTO_VAL_INIT(b);
|
||||
if (!parse_monero_block_from_blob(input, b)) return THROW_ERROR_EXCEPTION("Failed to parse block");
|
||||
if (!get_monero_block_hash(b, block_id)) return THROW_ERROR_EXCEPTION("Failed to calculate hash for block");
|
||||
} else {
|
||||
block b = AUTO_VAL_INIT(b);
|
||||
b.set_blob_type(blob_type);
|
||||
if (!parse_and_validate_block_from_blob(input, b)) return THROW_ERROR_EXCEPTION("Failed to parse block");
|
||||
if (!get_block_hash(b, block_id)) return THROW_ERROR_EXCEPTION("Failed to calculate hash for block");
|
||||
}
|
||||
|
||||
char *cstr = reinterpret_cast<char*>(&block_id);
|
||||
v8::Local<v8::Value> returnValue = Nan::CopyBuffer(cstr, 32).ToLocalChecked();
|
||||
@@ -206,12 +267,18 @@ NAN_METHOD(construct_block_blob) { // (parentBlockTemplateBuffer, nonceBuffer, c
|
||||
blobdata block_template_blob = std::string(Buffer::Data(block_template_buf), Buffer::Length(block_template_buf));
|
||||
blobdata output = "";
|
||||
|
||||
block b = AUTO_VAL_INIT(b);
|
||||
b.set_blob_type(blob_type);
|
||||
if (!parse_and_validate_block_from_blob(block_template_blob, b)) return THROW_ERROR_EXCEPTION("Failed to parse block");
|
||||
|
||||
b.nonce = nonce;
|
||||
if (!block_to_blob(b, output)) return THROW_ERROR_EXCEPTION("Failed to convert block to blob");
|
||||
if (blob_type == BLOB_TYPE_CRYPTONOTE) {
|
||||
monero_block_t b = AUTO_VAL_INIT(b);
|
||||
if (!parse_monero_block_from_blob(block_template_blob, b)) return THROW_ERROR_EXCEPTION("Failed to parse block");
|
||||
b.nonce = nonce;
|
||||
if (!monero_block_to_blob(b, output)) return THROW_ERROR_EXCEPTION("Failed to convert block to blob");
|
||||
} else {
|
||||
block b = AUTO_VAL_INIT(b);
|
||||
b.set_blob_type(blob_type);
|
||||
if (!parse_and_validate_block_from_blob(block_template_blob, b)) return THROW_ERROR_EXCEPTION("Failed to parse block");
|
||||
b.nonce = nonce;
|
||||
if (!block_to_blob(b, output)) return THROW_ERROR_EXCEPTION("Failed to convert block to blob");
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> returnValue = Nan::CopyBuffer((char*)output.data(), output.size()).ToLocalChecked();
|
||||
info.GetReturnValue().Set(returnValue);
|
||||
|
||||
Reference in New Issue
Block a user