Cryptonote tools WIP

This commit is contained in:
SChernykh 2021-06-15 00:28:32 +02:00
parent 0414511de0
commit 7bfb801ce2
12 changed files with 644 additions and 73 deletions

View file

@ -76,6 +76,9 @@ set(HEADERS_BASE
src/base/tools/Handle.h
src/base/tools/String.h
src/base/tools/Timer.h
src/base/tools/cryptonote/BlobReader.h
src/base/tools/cryptonote/BlockTemplate.h
src/base/tools/cryptonote/WalletAddress.h
)
set(SOURCES_BASE
@ -126,6 +129,8 @@ set(SOURCES_BASE
src/base/tools/Cvt.cpp
src/base/tools/String.cpp
src/base/tools/Timer.cpp
src/base/tools/cryptonote/BlockTemplate.cpp
src/base/tools/cryptonote/WalletAddress.cpp
)

View file

@ -0,0 +1,101 @@
/* XMRig
* Copyright 2014-2021 The Monero Project
* Copyright 2018-2021 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2021 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef XMRIG_BLOBREADER_H
#define XMRIG_BLOBREADER_H
#include <cstdint>
namespace xmrig {
class CBlobReader
{
public:
inline CBlobReader(const void* data, size_t size)
: m_data(reinterpret_cast<const uint8_t*>(data))
, m_size(size)
, m_index(0)
{}
inline bool operator()(uint8_t& data) { return getByte(data); }
inline bool operator()(uint64_t& data) { return getVarint(data); }
template<size_t N>
inline bool operator()(uint8_t(&data)[N])
{
for (size_t i = 0; i < N; ++i) {
if (!getByte(data[i])) {
return false;
}
}
return true;
}
template<typename T>
inline void readItems(T& data, size_t count)
{
data.resize(count);
for (size_t i = 0; i < count; ++i)
operator()(data[i]);
}
inline size_t index() const { return m_index; }
private:
inline bool getByte(uint8_t& data)
{
if (m_index >= m_size) {
return false;
}
data = m_data[m_index++];
return true;
}
inline bool getVarint(uint64_t& data)
{
uint64_t result = 0;
uint8_t t;
int shift = 0;
do {
if (!getByte(t)) {
return false;
}
result |= static_cast<uint64_t>(t & 0x7F) << shift;
shift += 7;
} while (t & 0x80);
data = result;
return true;
}
const uint8_t* m_data;
size_t m_size;
size_t m_index;
};
} /* namespace xmrig */
#endif /* XMRIG_BLOBREADER_H */

View file

@ -0,0 +1,215 @@
/* XMRig
* Copyright 2014-2021 The Monero Project
* Copyright 2018-2021 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2021 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* 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"
#pragma optimize("", off)
namespace xmrig {
bool CBlockTemplate::Init(const String& blockTemplate)
{
raw_blob = Cvt::fromHex(blockTemplate);
CBlobReader ar(raw_blob.data(), raw_blob.size());
// Block header
ar(major_version);
ar(minor_version);
ar(timestamp);
ar(prev_id);
ar(nonce);
// 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);
tx_pubkey_index = ar.index() + 1;
ar.readItems(extra, extra_size);
// First thing in tx_extra must be TX_EXTRA_TAG_PUBKEY
if (extra[0] != 0x01)
return false;
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);
hashes.resize((num_hashes + 1) * HASH_SIZE);
CalculateMinerTxHash(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(hashes.data(), num_hashes + 1, root_hash);
CalculateHashingBlob();
return true;
}
void CBlockTemplate::CalculateMinerTxHash(uint8_t* hash)
{
uint8_t hashes[HASH_SIZE * 3];
uint8_t md[200];
// Calculate 3 partial hashes
// 1. Prefix
keccak(raw_blob.data() + miner_tx_prefix_begin_index, miner_tx_prefix_end_index - miner_tx_prefix_begin_index, md);
memcpy(hashes, md, HASH_SIZE);
// 2. Base RCT, single 0 byte in miner tx
static const uint8_t known_second_hash[HASH_SIZE] = {
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);
// 3. Prunable RCT, empty in miner tx
memset(hashes + HASH_SIZE * 2, 0, HASH_SIZE);
// Calculate miner transaction hash
keccak(hashes, sizeof(hashes), md);
memcpy(hash, md, HASH_SIZE);
}
void CBlockTemplate::CalculateMerkleTreeHash(const uint8_t* hashes, size_t count, uint8_t* root_hash)
{
uint8_t md[200];
if (count == 1) {
memcpy(root_hash, hashes, HASH_SIZE);
}
else if (count == 2) {
keccak(hashes, HASH_SIZE * 2, md);
memcpy(root_hash, md, HASH_SIZE);
}
else {
size_t i, j;
size_t cnt = count;
while (cnt & (cnt - 1)) cnt &= cnt - 1;
Buffer ints(HASH_SIZE);
memcpy(ints.data(), hashes, (cnt * 2 - count) * HASH_SIZE);
for (i = cnt * 2 - count, j = cnt * 2 - count; j < cnt; i += 2, ++j) {
keccak(hashes + i * HASH_SIZE, HASH_SIZE * 2, md);
memcpy(ints.data() + j * HASH_SIZE, md, HASH_SIZE);
}
while (cnt > 2) {
cnt >>= 1;
for (i = 0, j = 0; j < cnt; i += 2, ++j) {
keccak(ints.data() + i * HASH_SIZE, HASH_SIZE * 2, md);
memcpy(ints.data() + j * HASH_SIZE, md, HASH_SIZE);
}
}
keccak(ints.data(), HASH_SIZE * 2, md);
memcpy(root_hash, md, HASH_SIZE);
}
}
void CBlockTemplate::CalculateHashingBlob()
{
hashingBlob.clear();
hashingBlob.reserve(miner_tx_prefix_begin_index + HASH_SIZE + 3);
hashingBlob.assign(raw_blob.begin(), raw_blob.begin() + miner_tx_prefix_begin_index);
hashingBlob.insert(hashingBlob.end(), root_hash, root_hash + HASH_SIZE);
uint64_t k = num_hashes + 1;
while (k >= 0x80) {
hashingBlob.emplace_back((static_cast<uint8_t>(k) & 0x7F) | 0x80);
k >>= 7;
}
hashingBlob.emplace_back(static_cast<uint8_t>(k));
for (int i = 0; i < hashingBlob.size(); ++i)
printf("%02x", hashingBlob[i]);
}
} /* namespace xmrig */

View file

@ -0,0 +1,86 @@
/* XMRig
* Copyright 2014-2021 The Monero Project
* Copyright 2018-2021 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2021 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef XMRIG_BLOCKTEMPLATE_H
#define XMRIG_BLOCKTEMPLATE_H
#include "base/tools/Buffer.h"
#include "base/tools/String.h"
namespace xmrig {
struct CBlockTemplate
{
enum {
HASH_SIZE = 32,
KEY_SIZE = 32,
NONCE_SIZE = 4,
};
Buffer raw_blob;
size_t eph_public_key_index;
size_t tx_pubkey_index;
size_t miner_tx_prefix_begin_index;
size_t miner_tx_prefix_end_index;
// Block header
uint8_t major_version;
uint8_t minor_version;
uint64_t timestamp;
uint8_t prev_id[HASH_SIZE];
uint8_t nonce[NONCE_SIZE];
// Miner tx
uint64_t tx_version;
uint64_t unlock_time;
uint64_t num_inputs;
uint8_t input_type;
uint64_t height;
uint64_t num_outputs;
uint64_t amount;
uint8_t output_type;
uint8_t eph_public_key[KEY_SIZE];
uint64_t extra_size;
Buffer extra;
uint8_t vin_rct_type;
// Transaction hashes
uint64_t num_hashes;
Buffer hashes;
uint8_t root_hash[HASH_SIZE];
Buffer hashingBlob;
bool Init(const String& blockTemplate);
private:
void CalculateMinerTxHash(uint8_t* hash);
static void CalculateMerkleTreeHash(const uint8_t* hashes, size_t count, uint8_t* root_hash);
void CalculateHashingBlob();
};
} /* namespace xmrig */
#endif /* XMRIG_BLOCKTEMPLATE_H */

View file

@ -0,0 +1,116 @@
/* XMRig
* Copyright 2014-2021 The Monero Project
* Copyright 2018-2021 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2021 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "base/crypto/keccak.h"
#include "base/tools/cryptonote/BlobReader.h"
#include "base/tools/cryptonote/WalletAddress.h"
#include "base/tools/Buffer.h"
#include "crypto/cn/umul128.h"
#include <array>
#pragma optimize("", off)
namespace xmrig {
bool WalletAddress::Decode(const String& address)
{
static constexpr std::array<int, 9> block_sizes{ 0, 2, 3, 5, 6, 7, 9, 10, 11 };
static constexpr char alphabet[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
constexpr size_t alphabet_size = sizeof(alphabet) - 1;
int8_t reverse_alphabet[256];
memset(reverse_alphabet, -1, sizeof(reverse_alphabet));
for (int i = 0; i < alphabet_size; ++i) {
reverse_alphabet[alphabet[i]] = i;
}
const int len = static_cast<int>(address.size());
const int num_full_blocks = len / block_sizes.back();
const int last_block_size = len % block_sizes.back();
int last_block_size_index = -1;
for (int i = 0; i < block_sizes.size(); ++i) {
if (block_sizes[i] == last_block_size) {
last_block_size_index = i;
break;
}
}
if (last_block_size_index < 0) {
return false;
}
Buffer data;
data.reserve(static_cast<size_t>(num_full_blocks) * sizeof(uint64_t) + last_block_size_index);
const char* address_data = address.data();
for (int i = 0; i <= num_full_blocks; ++i) {
uint64_t num = 0;
uint64_t order = 1;
for (int j = ((i < num_full_blocks) ? block_sizes.back() : last_block_size) - 1; j >= 0; --j) {
const int digit = reverse_alphabet[address_data[j]];
if (digit < 0) {
return false;
}
uint64_t hi;
const uint64_t tmp = num + __umul128(order, static_cast<uint64_t>(digit), &hi);
if ((tmp < num) || hi) {
return false;
}
num = tmp;
order *= alphabet_size;
}
address_data += block_sizes.back();
uint8_t* p = reinterpret_cast<uint8_t*>(&num);
for (int j = ((i < num_full_blocks) ? sizeof(num) : last_block_size_index) - 1; j >= 0; --j) {
data.emplace_back(p[j]);
}
}
CBlobReader ar(data.data(), data.size());
ar(tag);
ar(public_spend_key);
ar(public_view_key);
ar(checksum);
uint8_t md[200];
keccak(data.data(), data.size() - sizeof(checksum), md);
if (memcmp(checksum, md, sizeof(checksum)) != 0) {
return false;
}
return true;
}
} /* namespace xmrig */

View file

@ -0,0 +1,44 @@
/* XMRig
* Copyright 2014-2021 The Monero Project
* Copyright 2018-2021 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2021 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef XMRIG_WALLETADDRESS_H
#define XMRIG_WALLETADDRESS_H
#include "base/tools/String.h"
namespace xmrig {
struct WalletAddress
{
uint64_t tag;
uint8_t public_spend_key[32];
uint8_t public_view_key[32];
uint8_t checksum[4];
bool Decode(const String& address);
};
} /* namespace xmrig */
#endif /* XMRIG_WALLETADDRESS_H */