Enforce deterministic tx keys starting from v15

pull/181/head
SChernykh 2022-06-28 10:32:21 +02:00
parent ee1c5a0a56
commit 14bbf96feb
7 changed files with 82 additions and 16 deletions

View File

@ -50,6 +50,8 @@ BlockTemplate::BlockTemplate(p2pool* pool)
, m_difficulty{} , m_difficulty{}
, m_seedHash{} , m_seedHash{}
, m_timestamp(0) , m_timestamp(0)
, m_txkeyPub{}
, m_txkeySec{}
, m_poolBlockTemplate(new PoolBlock()) , m_poolBlockTemplate(new PoolBlock())
, m_finalReward(0) , m_finalReward(0)
{ {
@ -73,8 +75,6 @@ BlockTemplate::BlockTemplate(p2pool* pool)
#if TEST_MEMPOOL_PICKING_ALGORITHM #if TEST_MEMPOOL_PICKING_ALGORITHM
m_knapsack.reserve(512 * 309375); m_knapsack.reserve(512 * 309375);
#endif #endif
update_tx_keys();
} }
BlockTemplate::~BlockTemplate() BlockTemplate::~BlockTemplate()
@ -197,6 +197,8 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
*this = *m_oldTemplates[id % array_size(&BlockTemplate::m_oldTemplates)]; *this = *m_oldTemplates[id % array_size(&BlockTemplate::m_oldTemplates)];
}; };
get_tx_keys(m_txkeyPub, m_txkeySec, miner_wallet->spend_public_key(), data.prev_id);
m_height = data.height; m_height = data.height;
m_difficulty = data.difficulty; m_difficulty = data.difficulty;
m_seedHash = data.seed_hash; m_seedHash = data.seed_hash;
@ -1057,13 +1059,6 @@ std::vector<uint8_t> BlockTemplate::get_block_template_blob(uint32_t template_id
return m_blockTemplateBlob; return m_blockTemplateBlob;
} }
void BlockTemplate::update_tx_keys()
{
WriteLock lock(m_lock);
generate_keys(m_txkeyPub, m_txkeySec);
}
void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce) void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce)
{ {
WriteLock lock(m_lock); WriteLock lock(m_lock);

View File

@ -47,7 +47,6 @@ public:
uint32_t get_hashing_blobs(uint32_t extra_nonce_start, uint32_t count, std::vector<uint8_t>& blobs, uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const; uint32_t get_hashing_blobs(uint32_t extra_nonce_start, uint32_t count, std::vector<uint8_t>& blobs, uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const;
std::vector<uint8_t> get_block_template_blob(uint32_t template_id, size_t& nonce_offset, size_t& extra_nonce_offset) const; std::vector<uint8_t> get_block_template_blob(uint32_t template_id, size_t& nonce_offset, size_t& extra_nonce_offset) const;
void update_tx_keys();
FORCEINLINE uint64_t height() const { return m_height; } FORCEINLINE uint64_t height() const { return m_height; }
FORCEINLINE difficulty_type difficulty() const { return m_difficulty; } FORCEINLINE difficulty_type difficulty() const { return m_difficulty; }

View File

@ -24,6 +24,10 @@ extern "C" {
#include "crypto-ops.h" #include "crypto-ops.h"
} }
// l = 2^252 + 27742317777372353535851937790883648493.
// l fits 15 times in 32 bytes (iow, 15 l is the highest multiple of l that fits in 32 bytes)
static constexpr uint8_t limit[32] = { 0xe3, 0x6a, 0x67, 0x72, 0x8b, 0xce, 0x13, 0x29, 0x8f, 0x30, 0x82, 0x8c, 0x0b, 0xa4, 0x10, 0x39, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0 };
namespace p2pool { namespace p2pool {
namespace { namespace {
@ -79,10 +83,6 @@ static FORCEINLINE bool less32(const uint8_t* k0, const uint8_t* k1)
// cppcheck-suppress constParameter // cppcheck-suppress constParameter
void generate_keys(hash& pub, hash& sec) void generate_keys(hash& pub, hash& sec)
{ {
// l = 2^252 + 27742317777372353535851937790883648493.
// l fits 15 times in 32 bytes (iow, 15 l is the highest multiple of l that fits in 32 bytes)
static constexpr uint8_t limit[32] = { 0xe3, 0x6a, 0x67, 0x72, 0x8b, 0xce, 0x13, 0x29, 0x8f, 0x30, 0x82, 0x8c, 0x0b, 0xa4, 0x10, 0x39, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0 };
do { do {
do { randomBytes(sec.h); } while (!less32(sec.h, limit)); do { randomBytes(sec.h); } while (!less32(sec.h, limit));
sc_reduce32(sec.h); sc_reduce32(sec.h);
@ -93,6 +93,30 @@ void generate_keys(hash& pub, hash& sec)
ge_p3_tobytes(pub.h, &point); ge_p3_tobytes(pub.h, &point);
} }
// cppcheck-suppress constParameter
void generate_keys_deterministic(hash& pub, hash& sec, const uint8_t* entropy, size_t len)
{
uint32_t counter = 0;
do {
do {
++counter;
keccak_custom([entropy, len, counter](int offset)
{
if (offset < static_cast<int>(len)) {
return entropy[offset];
}
return static_cast<uint8_t>(counter >> ((offset - len) * 8));
}, static_cast<int>(len + sizeof(counter)), sec.h, HASH_SIZE);
} while (!less32(sec.h, limit));
sc_reduce32(sec.h);
} while (!sc_isnonzero(sec.h));
ge_p3 point;
ge_scalarmult_base(&point, sec.h);
ge_p3_tobytes(pub.h, &point);
}
bool check_keys(const hash& pub, const hash& sec) bool check_keys(const hash& pub, const hash& sec)
{ {
// From ge_scalarmult_base's comment: "preconditions a[31] <= 127" // From ge_scalarmult_base's comment: "preconditions a[31] <= 127"
@ -226,12 +250,45 @@ public:
return true; return true;
} }
void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id)
{
std::array<uint8_t, HASH_SIZE * 2> index;
memcpy(index.data(), wallet_spend_key.h, HASH_SIZE);
memcpy(index.data() + HASH_SIZE, monero_block_id.h, HASH_SIZE);
{
MutexLock lock(m);
auto it = tx_keys.find(index);
if (it != tx_keys.end()) {
pub = it->second.first;
sec = it->second.second;
return;
}
}
static constexpr char domain[] = "tx_secret_key";
static constexpr size_t N = sizeof(domain) - 1;
uint8_t entropy[N + HASH_SIZE * 2];
memcpy(entropy, domain, N);
memcpy(entropy + N, wallet_spend_key.h, HASH_SIZE);
memcpy(entropy + N + HASH_SIZE, monero_block_id.h, HASH_SIZE);
generate_keys_deterministic(pub, sec, entropy, sizeof(entropy));
{
MutexLock lock(m);
tx_keys.emplace(index, std::pair<hash, hash>(pub, sec));
}
}
void clear() void clear()
{ {
MutexLock lock(m); MutexLock lock(m);
derivations.clear(); derivations.clear();
public_keys.clear(); public_keys.clear();
tx_keys.clear();
} }
private: private:
@ -245,6 +302,7 @@ private:
uv_mutex_t m; uv_mutex_t m;
unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, DerivationEntry> derivations; unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, DerivationEntry> derivations;
unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> public_keys; unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> public_keys;
unordered_map<std::array<uint8_t, HASH_SIZE * 2>, std::pair<hash, hash>> tx_keys;
}; };
static Cache* cache = nullptr; static Cache* cache = nullptr;
@ -259,6 +317,11 @@ bool derive_public_key(const hash& derivation, size_t output_index, const hash&
return cache->get_public_key(derivation, output_index, base, derived_key); return cache->get_public_key(derivation, output_index, base, derived_key);
} }
void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id)
{
cache->get_tx_keys(pub, sec, wallet_spend_key, monero_block_id);
}
void derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_tag) void derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_tag)
{ {
constexpr uint8_t salt[] = "view_tag"; constexpr uint8_t salt[] = "view_tag";

View File

@ -20,6 +20,8 @@
namespace p2pool { namespace p2pool {
void generate_keys(hash& pub, hash& sec); void generate_keys(hash& pub, hash& sec);
void generate_keys_deterministic(hash& pub, hash& sec, const uint8_t* entropy, size_t len);
void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id);
bool check_keys(const hash& pub, const hash& sec); bool check_keys(const hash& pub, const hash& sec);
bool generate_key_derivation(const hash& key1, const hash& key2, size_t output_index, hash& derivation, uint8_t& view_tag); bool generate_key_derivation(const hash& key1, const hash& key2, size_t output_index, hash& derivation, uint8_t& view_tag);
bool derive_public_key(const hash& derivation, size_t output_index, const hash& base, hash& derived_key); bool derive_public_key(const hash& derivation, size_t output_index, const hash& base, hash& derived_key);

View File

@ -198,7 +198,6 @@ void Miner::run(WorkerData* data)
if (j.m_diff.check_pow(h)) { if (j.m_diff.check_pow(h)) {
LOGINFO(0, log::Green() << "worker thread " << data->m_index << '/' << data->m_count << " found a mainchain block, submitting it"); LOGINFO(0, log::Green() << "worker thread " << data->m_index << '/' << data->m_count << " found a mainchain block, submitting it");
m_pool->submit_block_async(j.m_templateId, j.m_nonce, j.m_extraNonce); m_pool->submit_block_async(j.m_templateId, j.m_nonce, j.m_extraNonce);
m_pool->block_template().update_tx_keys();
} }
if (j.m_sidechainDiff.check_pow(h)) { if (j.m_sidechainDiff.check_pow(h)) {

View File

@ -229,6 +229,15 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
return __LINE__; return __LINE__;
} }
// Enforce deterministic tx keys starting from v15
if (m_majorVersion >= HARDFORK_VIEW_TAGS_VERSION) {
hash pub, sec;
get_tx_keys(pub, sec, spend_pub_key, m_prevId);
if ((pub != m_txkeyPub) || (sec != m_txkeySec)) {
return __LINE__;
}
}
READ_BUF(m_parent.h, HASH_SIZE); READ_BUF(m_parent.h, HASH_SIZE);
uint64_t num_uncles; uint64_t num_uncles;

View File

@ -374,7 +374,6 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
const char* s = client->m_customUser; const char* s = client->m_customUser;
LOGINFO(0, log::Green() << "client " << static_cast<char*>(client->m_addrString) << (*s ? " user " : "") << s << " found a mainchain block, submitting it"); LOGINFO(0, log::Green() << "client " << static_cast<char*>(client->m_addrString) << (*s ? " user " : "") << s << " found a mainchain block, submitting it");
m_pool->submit_block_async(template_id, nonce, extra_nonce); m_pool->submit_block_async(template_id, nonce, extra_nonce);
block.update_tx_keys();
} }
SubmittedShare* share; SubmittedShare* share;