Update BlockTemplate class.
This commit is contained in:
parent
a28f411339
commit
bea2a6cf5b
11 changed files with 598 additions and 257 deletions
|
@ -18,177 +18,64 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "base/crypto/keccak.h"
|
||||
#include "base/tools/Cvt.h"
|
||||
#include "base/tools/cryptonote/BlobReader.h"
|
||||
#include "base/tools/cryptonote/BlockTemplate.h"
|
||||
#include "3rdparty/rapidjson/document.h"
|
||||
#include "base/crypto/keccak.h"
|
||||
#include "base/tools/cryptonote/BlobReader.h"
|
||||
#include "base/tools/Cvt.h"
|
||||
|
||||
|
||||
namespace xmrig {
|
||||
|
||||
|
||||
bool BlockTemplate::Init(const String& blockTemplate, Coin coin)
|
||||
void xmrig::BlockTemplate::calculateMinerTxHash(const uint8_t *prefix_begin, const uint8_t *prefix_end, uint8_t *hash)
|
||||
{
|
||||
raw_blob = Cvt::fromHex(blockTemplate);
|
||||
|
||||
BlobReader ar(raw_blob.data(), raw_blob.size());
|
||||
|
||||
// Block header
|
||||
ar(major_version);
|
||||
ar(minor_version);
|
||||
ar(timestamp);
|
||||
ar(prev_id);
|
||||
ar(nonce);
|
||||
|
||||
// Wownero block template has miner signature starting from version 18
|
||||
has_miner_signature = (coin == Coin::WOWNERO) && (major_version >= 18);
|
||||
if (has_miner_signature) {
|
||||
ar(miner_signature);
|
||||
ar(vote);
|
||||
}
|
||||
|
||||
// Miner transaction begin
|
||||
// Prefix begin
|
||||
miner_tx_prefix_begin_index = ar.index();
|
||||
|
||||
ar(tx_version);
|
||||
ar(unlock_time);
|
||||
ar(num_inputs);
|
||||
|
||||
// must be 1 input
|
||||
if (num_inputs != 1)
|
||||
return false;
|
||||
|
||||
ar(input_type);
|
||||
|
||||
// input type must be txin_gen (0xFF)
|
||||
if (input_type != 0xFF)
|
||||
return false;
|
||||
|
||||
ar(height);
|
||||
|
||||
ar(num_outputs);
|
||||
|
||||
// must be 1 output
|
||||
if (num_outputs != 1)
|
||||
return false;
|
||||
|
||||
ar(amount);
|
||||
ar(output_type);
|
||||
|
||||
// output type must be txout_to_key (2)
|
||||
if (output_type != 2)
|
||||
return false;
|
||||
|
||||
eph_public_key_index = ar.index();
|
||||
|
||||
ar(eph_public_key);
|
||||
ar(extra_size);
|
||||
|
||||
const uint64_t tx_extra_index = ar.index();
|
||||
|
||||
ar.readItems(extra, extra_size);
|
||||
|
||||
BlobReader ar_extra(extra.data(), extra_size);
|
||||
|
||||
tx_extra_nonce_size = 0;
|
||||
tx_extra_nonce_index = 0;
|
||||
|
||||
while (ar_extra.index() < extra_size) {
|
||||
uint64_t extra_tag = 0;
|
||||
ar_extra(extra_tag);
|
||||
|
||||
switch (extra_tag) {
|
||||
case 0x01: // TX_EXTRA_TAG_PUBKEY
|
||||
tx_pubkey_index = tx_extra_index + ar_extra.index();
|
||||
ar_extra.skip(KEY_SIZE);
|
||||
break;
|
||||
|
||||
case 0x02: // TX_EXTRA_NONCE
|
||||
ar_extra(tx_extra_nonce_size);
|
||||
tx_extra_nonce_index = tx_extra_index + ar_extra.index();
|
||||
ar_extra.skip(tx_extra_nonce_size);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false; // TODO: handle other tags
|
||||
}
|
||||
}
|
||||
|
||||
miner_tx_prefix_end_index = ar.index();
|
||||
// Prefix end
|
||||
|
||||
// RCT signatures (empty in miner transaction)
|
||||
ar(vin_rct_type);
|
||||
|
||||
// must be RCTTypeNull (0)
|
||||
if (vin_rct_type != 0)
|
||||
return false;
|
||||
|
||||
const size_t miner_tx_end = ar.index();
|
||||
// Miner transaction end
|
||||
|
||||
// Miner transaction must have exactly 1 byte with value 0 after the prefix
|
||||
if ((miner_tx_end != miner_tx_prefix_end_index + 1) || (raw_blob[miner_tx_prefix_end_index] != 0))
|
||||
return false;
|
||||
|
||||
// Other transaction hashes
|
||||
ar(num_hashes);
|
||||
|
||||
# ifdef XMRIG_PROXY_PROJECT
|
||||
hashes.resize((num_hashes + 1) * HASH_SIZE);
|
||||
CalculateMinerTxHash(raw_blob.data() + miner_tx_prefix_begin_index, raw_blob.data() + miner_tx_prefix_end_index, hashes.data());
|
||||
|
||||
for (uint64_t i = 1; i <= num_hashes; ++i) {
|
||||
uint8_t h[HASH_SIZE];
|
||||
ar(h);
|
||||
memcpy(hashes.data() + i * HASH_SIZE, h, HASH_SIZE);
|
||||
}
|
||||
|
||||
CalculateMerkleTreeHash();
|
||||
# endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void BlockTemplate::CalculateMinerTxHash(const uint8_t* prefix_begin, const uint8_t* prefix_end, uint8_t* hash)
|
||||
{
|
||||
uint8_t hashes[HASH_SIZE * 3];
|
||||
uint8_t hashes[kHashSize * 3];
|
||||
|
||||
// Calculate 3 partial hashes
|
||||
|
||||
// 1. Prefix
|
||||
keccak(prefix_begin, static_cast<int>(prefix_end - prefix_begin), hashes, HASH_SIZE);
|
||||
keccak(prefix_begin, static_cast<int>(prefix_end - prefix_begin), hashes, kHashSize);
|
||||
|
||||
// 2. Base RCT, single 0 byte in miner tx
|
||||
static const uint8_t known_second_hash[HASH_SIZE] = {
|
||||
static const uint8_t known_second_hash[kHashSize] = {
|
||||
188,54,120,158,122,30,40,20,54,70,66,41,130,143,129,125,102,18,247,180,119,214,101,145,255,150,169,224,100,188,201,138
|
||||
};
|
||||
memcpy(hashes + HASH_SIZE, known_second_hash, HASH_SIZE);
|
||||
memcpy(hashes + kHashSize, known_second_hash, kHashSize);
|
||||
|
||||
// 3. Prunable RCT, empty in miner tx
|
||||
memset(hashes + HASH_SIZE * 2, 0, HASH_SIZE);
|
||||
memset(hashes + kHashSize * 2, 0, kHashSize);
|
||||
|
||||
// Calculate miner transaction hash
|
||||
keccak(hashes, sizeof(hashes), hash, HASH_SIZE);
|
||||
keccak(hashes, sizeof(hashes), hash, kHashSize);
|
||||
}
|
||||
|
||||
|
||||
void BlockTemplate::CalculateMerkleTreeHash()
|
||||
void xmrig::BlockTemplate::calculateRootHash(const uint8_t *prefix_begin, const uint8_t *prefix_end, const Buffer &miner_tx_merkle_tree_branch, uint8_t *root_hash)
|
||||
{
|
||||
miner_tx_merkle_tree_branch.clear();
|
||||
calculateMinerTxHash(prefix_begin, prefix_end, root_hash);
|
||||
|
||||
const uint64_t count = num_hashes + 1;
|
||||
uint8_t* h = hashes.data();
|
||||
for (size_t i = 0; i < miner_tx_merkle_tree_branch.size(); i += kHashSize) {
|
||||
uint8_t h[kHashSize * 2];
|
||||
|
||||
memcpy(h, root_hash, kHashSize);
|
||||
memcpy(h + kHashSize, miner_tx_merkle_tree_branch.data() + i, kHashSize);
|
||||
|
||||
keccak(h, kHashSize * 2, root_hash, kHashSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void xmrig::BlockTemplate::calculateMerkleTreeHash()
|
||||
{
|
||||
m_minerTxMerkleTreeBranch.clear();
|
||||
|
||||
const uint64_t count = m_numHashes + 1;
|
||||
const uint8_t *h = m_hashes.data();
|
||||
|
||||
if (count == 1) {
|
||||
memcpy(root_hash, h, HASH_SIZE);
|
||||
memcpy(m_rootHash, h, kHashSize);
|
||||
}
|
||||
else if (count == 2) {
|
||||
miner_tx_merkle_tree_branch.insert(miner_tx_merkle_tree_branch.end(), h + HASH_SIZE, h + HASH_SIZE * 2);
|
||||
keccak(h, HASH_SIZE * 2, root_hash, HASH_SIZE);
|
||||
m_minerTxMerkleTreeBranch.insert(m_minerTxMerkleTreeBranch.end(), h + kHashSize, h + kHashSize * 2);
|
||||
keccak(h, kHashSize * 2, m_rootHash, kHashSize);
|
||||
}
|
||||
else {
|
||||
size_t i, j, cnt;
|
||||
|
@ -197,64 +84,222 @@ void BlockTemplate::CalculateMerkleTreeHash()
|
|||
|
||||
cnt >>= 1;
|
||||
|
||||
miner_tx_merkle_tree_branch.reserve(HASH_SIZE * (i - 1));
|
||||
m_minerTxMerkleTreeBranch.reserve(kHashSize * (i - 1));
|
||||
|
||||
Buffer ints(cnt * HASH_SIZE);
|
||||
memcpy(ints.data(), h, (cnt * 2 - count) * HASH_SIZE);
|
||||
Buffer ints(cnt * kHashSize);
|
||||
memcpy(ints.data(), h, (cnt * 2 - count) * kHashSize);
|
||||
|
||||
for (i = cnt * 2 - count, j = cnt * 2 - count; j < cnt; i += 2, ++j) {
|
||||
if (i == 0) {
|
||||
miner_tx_merkle_tree_branch.insert(miner_tx_merkle_tree_branch.end(), h + HASH_SIZE, h + HASH_SIZE * 2);
|
||||
m_minerTxMerkleTreeBranch.insert(m_minerTxMerkleTreeBranch.end(), h + kHashSize, h + kHashSize * 2);
|
||||
}
|
||||
keccak(h + i * HASH_SIZE, HASH_SIZE * 2, ints.data() + j * HASH_SIZE, HASH_SIZE);
|
||||
keccak(h + i * kHashSize, kHashSize * 2, ints.data() + j * kHashSize, kHashSize);
|
||||
}
|
||||
|
||||
while (cnt > 2) {
|
||||
cnt >>= 1;
|
||||
for (i = 0, j = 0; j < cnt; i += 2, ++j) {
|
||||
if (i == 0) {
|
||||
miner_tx_merkle_tree_branch.insert(miner_tx_merkle_tree_branch.end(), ints.data() + HASH_SIZE, ints.data() + HASH_SIZE * 2);
|
||||
m_minerTxMerkleTreeBranch.insert(m_minerTxMerkleTreeBranch.end(), ints.data() + kHashSize, ints.data() + kHashSize * 2);
|
||||
}
|
||||
keccak(ints.data() + i * HASH_SIZE, HASH_SIZE * 2, ints.data() + j * HASH_SIZE, HASH_SIZE);
|
||||
keccak(ints.data() + i * kHashSize, kHashSize * 2, ints.data() + j * kHashSize, kHashSize);
|
||||
}
|
||||
}
|
||||
|
||||
miner_tx_merkle_tree_branch.insert(miner_tx_merkle_tree_branch.end(), ints.data() + HASH_SIZE, ints.data() + HASH_SIZE * 2);
|
||||
keccak(ints.data(), HASH_SIZE * 2, root_hash, HASH_SIZE);
|
||||
m_minerTxMerkleTreeBranch.insert(m_minerTxMerkleTreeBranch.end(), ints.data() + kHashSize, ints.data() + kHashSize * 2);
|
||||
keccak(ints.data(), kHashSize * 2, m_rootHash, kHashSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BlockTemplate::CalculateRootHash(const uint8_t* prefix_begin, const uint8_t* prefix_end, const Buffer& miner_tx_merkle_tree_branch, uint8_t* root_hash)
|
||||
bool xmrig::BlockTemplate::parse(const Buffer &blocktemplate, const Coin &coin, bool hashes)
|
||||
{
|
||||
CalculateMinerTxHash(prefix_begin, prefix_end, root_hash);
|
||||
|
||||
for (size_t i = 0; i < miner_tx_merkle_tree_branch.size(); i += HASH_SIZE) {
|
||||
uint8_t h[HASH_SIZE * 2];
|
||||
|
||||
memcpy(h, root_hash, HASH_SIZE);
|
||||
memcpy(h + HASH_SIZE, miner_tx_merkle_tree_branch.data() + i, HASH_SIZE);
|
||||
|
||||
keccak(h, HASH_SIZE * 2, root_hash, HASH_SIZE);
|
||||
if (blocktemplate.size() < kMinSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_blob = blocktemplate;
|
||||
m_coin = coin;
|
||||
bool rc = false;
|
||||
|
||||
try {
|
||||
rc = parse(hashes);
|
||||
} catch (...) {}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void BlockTemplate::GenerateHashingBlob()
|
||||
bool xmrig::BlockTemplate::parse(const char *blocktemplate, size_t size, const Coin &coin, bool hashes)
|
||||
{
|
||||
hashingBlob.clear();
|
||||
hashingBlob.reserve(miner_tx_prefix_begin_index + HASH_SIZE + 3);
|
||||
if (size < (kMinSize * 2) || !Cvt::fromHex(m_blob, blocktemplate, size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
hashingBlob.assign(raw_blob.begin(), raw_blob.begin() + miner_tx_prefix_begin_index);
|
||||
hashingBlob.insert(hashingBlob.end(), root_hash, root_hash + HASH_SIZE);
|
||||
m_coin = coin;
|
||||
bool rc = false;
|
||||
|
||||
uint64_t k = num_hashes + 1;
|
||||
try {
|
||||
rc = parse(hashes);
|
||||
} catch (...) {}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
bool xmrig::BlockTemplate::parse(const rapidjson::Value &blocktemplate, const Coin &coin, bool hashes)
|
||||
{
|
||||
return blocktemplate.IsString() && parse(blocktemplate.GetString(), blocktemplate.GetStringLength(), coin, hashes);
|
||||
}
|
||||
|
||||
|
||||
bool xmrig::BlockTemplate::parse(const String &blocktemplate, const Coin &coin, bool hashes)
|
||||
{
|
||||
return parse(blocktemplate.data(), blocktemplate.size(), coin, hashes);
|
||||
}
|
||||
|
||||
|
||||
void xmrig::BlockTemplate::generateHashingBlob(Buffer &out) const
|
||||
{
|
||||
out.clear();
|
||||
out.reserve(offset(MINER_TX_PREFIX_OFFSET) + kHashSize + 3);
|
||||
|
||||
out.assign(m_blob.begin(), m_blob.begin() + offset(MINER_TX_PREFIX_OFFSET));
|
||||
out.insert(out.end(), m_rootHash, m_rootHash + kHashSize);
|
||||
|
||||
uint64_t k = m_numHashes + 1;
|
||||
while (k >= 0x80) {
|
||||
hashingBlob.emplace_back((static_cast<uint8_t>(k) & 0x7F) | 0x80);
|
||||
out.emplace_back((static_cast<uint8_t>(k) & 0x7F) | 0x80);
|
||||
k >>= 7;
|
||||
}
|
||||
hashingBlob.emplace_back(static_cast<uint8_t>(k));
|
||||
out.emplace_back(static_cast<uint8_t>(k));
|
||||
}
|
||||
|
||||
|
||||
} /* namespace xmrig */
|
||||
bool xmrig::BlockTemplate::parse(bool hashes)
|
||||
{
|
||||
BlobReader<true> ar(m_blob.data(), m_blob.size());
|
||||
|
||||
// Block header
|
||||
ar(m_version.first);
|
||||
ar(m_version.second);
|
||||
ar(m_timestamp);
|
||||
ar(m_prevId, kHashSize);
|
||||
|
||||
setOffset(NONCE_OFFSET, ar.index());
|
||||
ar.skip(kNonceSize);
|
||||
|
||||
// Wownero block template has miner signature starting from version 18
|
||||
if (m_coin == Coin::WOWNERO && majorVersion() >= 18) {
|
||||
ar(m_minerSignature, kSignatureSize);
|
||||
ar(m_vote);
|
||||
}
|
||||
|
||||
// Miner transaction begin
|
||||
// Prefix begin
|
||||
setOffset(MINER_TX_PREFIX_OFFSET, ar.index());
|
||||
|
||||
ar(m_txVersion);
|
||||
ar(m_unlockTime);
|
||||
ar(m_numInputs);
|
||||
|
||||
// must be 1 input
|
||||
if (m_numInputs != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ar(m_inputType);
|
||||
|
||||
// input type must be txin_gen (0xFF)
|
||||
if (m_inputType != 0xFF) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ar(m_height);
|
||||
ar(m_numOutputs);
|
||||
|
||||
// must be 1 output
|
||||
if (m_numOutputs != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ar(m_amount);
|
||||
ar(m_outputType);
|
||||
|
||||
// output type must be txout_to_key (2)
|
||||
if (m_outputType != 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
setOffset(EPH_PUBLIC_KEY_OFFSET, ar.index());
|
||||
|
||||
ar(m_ephPublicKey, kKeySize);
|
||||
ar(m_extraSize);
|
||||
|
||||
setOffset(TX_EXTRA_OFFSET, ar.index());
|
||||
|
||||
BlobReader<true> ar_extra(blob(TX_EXTRA_OFFSET), m_extraSize);
|
||||
ar.skip(m_extraSize);
|
||||
|
||||
while (ar_extra.index() < m_extraSize) {
|
||||
uint64_t extra_tag = 0;
|
||||
ar_extra(extra_tag);
|
||||
|
||||
switch (extra_tag) {
|
||||
case 0x01: // TX_EXTRA_TAG_PUBKEY
|
||||
setOffset(TX_PUBKEY_OFFSET, offset(TX_EXTRA_OFFSET) + ar_extra.index());
|
||||
ar_extra.skip(kKeySize);
|
||||
break;
|
||||
|
||||
case 0x02: // TX_EXTRA_NONCE
|
||||
{
|
||||
uint64_t size = 0;
|
||||
ar_extra(size);
|
||||
setOffset(TX_EXTRA_NONCE_OFFSET, offset(TX_EXTRA_OFFSET) + ar_extra.index());
|
||||
ar_extra(m_txExtraNonce, size);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return false; // TODO: handle other tags
|
||||
}
|
||||
}
|
||||
|
||||
setOffset(MINER_TX_PREFIX_END_OFFSET, ar.index());
|
||||
// Prefix end
|
||||
|
||||
// RCT signatures (empty in miner transaction)
|
||||
uint8_t vin_rct_type = 0;
|
||||
ar(vin_rct_type);
|
||||
|
||||
// must be RCTTypeNull (0)
|
||||
if (vin_rct_type != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t miner_tx_end = ar.index();
|
||||
// Miner transaction end
|
||||
|
||||
// Miner transaction must have exactly 1 byte with value 0 after the prefix
|
||||
if ((miner_tx_end != offset(MINER_TX_PREFIX_END_OFFSET) + 1) || (*blob(MINER_TX_PREFIX_END_OFFSET) != 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Other transaction hashes
|
||||
ar(m_numHashes);
|
||||
|
||||
if (hashes) {
|
||||
m_hashes.resize((m_numHashes + 1) * kHashSize);
|
||||
calculateMinerTxHash(blob(MINER_TX_PREFIX_OFFSET), blob(MINER_TX_PREFIX_END_OFFSET), m_hashes.data());
|
||||
|
||||
for (uint64_t i = 1; i <= m_numHashes; ++i) {
|
||||
Span h;
|
||||
ar(h, kHashSize);
|
||||
memcpy(m_hashes.data() + i * kHashSize, h.data(), kHashSize);
|
||||
}
|
||||
|
||||
calculateMerkleTreeHash();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue