Skip to content

Commit

Permalink
View tags support
Browse files Browse the repository at this point in the history
  • Loading branch information
SChernykh committed Apr 3, 2022
1 parent 1b3c1eb commit 67e3836
Show file tree
Hide file tree
Showing 13 changed files with 233 additions and 45 deletions.
14 changes: 11 additions & 3 deletions src/block_template.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -689,25 +689,33 @@ int BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Mine
m_poolBlockTemplate->m_outputs.clear();
m_poolBlockTemplate->m_outputs.reserve(num_outputs);

const uint8_t tx_type = m_poolBlockTemplate->get_tx_type();

uint64_t reward_amounts_weight = 0;
for (size_t i = 0; i < num_outputs; ++i) {
writeVarint(m_rewards[i], [this, &reward_amounts_weight](uint8_t b)
{
m_minerTx.push_back(b);
++reward_amounts_weight;
});
m_minerTx.push_back(TXOUT_TO_KEY);
m_minerTx.push_back(tx_type);

uint8_t view_tag = 0;

if (dry_run) {
m_minerTx.insert(m_minerTx.end(), HASH_SIZE, 0);
}
else {
hash eph_public_key;
if (!shares[i].m_wallet->get_eph_public_key(m_txkeySec, i, eph_public_key)) {
if (!shares[i].m_wallet->get_eph_public_key(m_txkeySec, i, eph_public_key, view_tag)) {
LOGERR(1, "get_eph_public_key failed at index " << i);
}
m_minerTx.insert(m_minerTx.end(), eph_public_key.h, eph_public_key.h + HASH_SIZE);
m_poolBlockTemplate->m_outputs.emplace_back(m_rewards[i], eph_public_key);
m_poolBlockTemplate->m_outputs.emplace_back(m_rewards[i], eph_public_key, tx_type, view_tag);
}

if (tx_type == TXOUT_TO_TAGGED_KEY) {
m_minerTx.emplace_back(view_tag);
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,16 @@
namespace p2pool {

constexpr size_t HASH_SIZE = 32;
constexpr uint8_t HARDFORK_SUPPORTED_VERSION = 14;
constexpr uint8_t HARDFORK_VIEW_TAGS_VERSION = 15;
constexpr uint8_t HARDFORK_SUPPORTED_VERSION = 16;
constexpr uint8_t MINER_REWARD_UNLOCK_TIME = 60;
constexpr uint8_t NONCE_SIZE = 4;
constexpr uint8_t EXTRA_NONCE_SIZE = 4;
constexpr uint8_t EXTRA_NONCE_MAX_SIZE = EXTRA_NONCE_SIZE + 10;
constexpr uint8_t TX_VERSION = 2;
constexpr uint8_t TXIN_GEN = 0xFF;
constexpr uint8_t TXOUT_TO_KEY = 2;
constexpr uint8_t TXOUT_TO_TAGGED_KEY = 3;
constexpr uint8_t TX_EXTRA_TAG_PUBKEY = 1;
constexpr uint8_t TX_EXTRA_NONCE = 2;
constexpr uint8_t TX_EXTRA_MERGE_MINING_TAG = 3;
Expand Down
40 changes: 33 additions & 7 deletions src/crypto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,17 +149,19 @@ class Cache
uv_mutex_destroy(&m);
}

bool get_derivation(const hash& key1, const hash& key2, hash& derivation)
bool get_derivation(const hash& key1, const hash& key2, size_t output_index, hash& derivation, uint8_t& view_tag)
{
std::array<uint8_t, HASH_SIZE * 2> index;
std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)> index;
memcpy(index.data(), key1.h, HASH_SIZE);
memcpy(index.data() + HASH_SIZE, key2.h, HASH_SIZE);
memcpy(index.data() + HASH_SIZE * 2, &output_index, sizeof(size_t));

{
MutexLock lock(m);
auto it = derivations.find(index);
if (it != derivations.end()) {
derivation = it->second;
derivation = it->second.derivation;
view_tag = it->second.view_tag;
return true;
}
}
Expand All @@ -177,9 +179,11 @@ class Cache
ge_p1p1_to_p2(&point2, &point3);
ge_tobytes(reinterpret_cast<uint8_t*>(&derivation), &point2);

derive_view_tag(derivation, output_index, view_tag);

{
MutexLock lock(m);
derivations.emplace(index, derivation);
derivations.emplace(index, DerivationEntry{ derivation, view_tag } );
}

return true;
Expand Down Expand Up @@ -236,23 +240,45 @@ class Cache
}

private:
struct DerivationEntry
{
hash derivation;
uint8_t view_tag;
};

uv_mutex_t m;
unordered_map<std::array<uint8_t, HASH_SIZE * 2>, hash> 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;
};

static Cache* cache = nullptr;

bool generate_key_derivation(const hash& key1, const hash& key2, hash& derivation)
bool generate_key_derivation(const hash& key1, const hash& key2, size_t output_index, hash& derivation, uint8_t& view_tag)
{
return cache->get_derivation(key1, key2, derivation);
return cache->get_derivation(key1, key2, output_index, derivation, view_tag);
}

bool derive_public_key(const hash& derivation, size_t output_index, const hash& base, hash& derived_key)
{
return cache->get_public_key(derivation, output_index, base, derived_key);
}

void derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_tag)
{
constexpr uint8_t salt[] = "view_tag";
constexpr size_t SALT_SIZE = sizeof(salt) - 1;

uint8_t buf[64];
memcpy(buf, salt, SALT_SIZE);
memcpy(buf + SALT_SIZE, derivation.h, HASH_SIZE);
uint8_t* p = buf + SALT_SIZE + HASH_SIZE;
writeVarint(output_index, [&p](uint8_t b) { *(p++) = b; });

hash view_tag_full;
keccak(buf, static_cast<int>(p - buf), view_tag_full.h, HASH_SIZE);
view_tag = view_tag_full.h[0];
}

void init_crypto_cache()
{
if (!cache) {
Expand Down
3 changes: 2 additions & 1 deletion src/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ namespace p2pool {

void generate_keys(hash& pub, hash& sec);
bool check_keys(const hash& pub, const hash& sec);
bool generate_key_derivation(const hash& key1, const hash& key2, hash& derivation);
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);
void derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_tag);

void init_crypto_cache();
void destroy_crypto_cache();
Expand Down
17 changes: 10 additions & 7 deletions src/p2pool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ p2pool::p2pool(int argc, char* argv[])
hash pub, sec, eph_public_key;
generate_keys(pub, sec);

if (!m_params->m_wallet.get_eph_public_key(sec, 0, eph_public_key)) {
uint8_t view_tag;
if (!m_params->m_wallet.get_eph_public_key(sec, 0, eph_public_key, view_tag)) {
LOGERR(1, "Invalid wallet address: get_eph_public_key failed");
panic();
}
Expand Down Expand Up @@ -596,13 +597,15 @@ void p2pool::download_block_headers(uint64_t current_height)
});
}

const uint64_t start_height = (current_height > BLOCK_HEADERS_REQUIRED) ? (current_height - BLOCK_HEADERS_REQUIRED) : 0;

s.m_pos = 0;
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_headers_range\",\"params\":{\"start_height\":" << current_height - BLOCK_HEADERS_REQUIRED << ",\"end_height\":" << current_height - 1 << "}}\0";
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_headers_range\",\"params\":{\"start_height\":" << start_height << ",\"end_height\":" << current_height - 1 << "}}\0";

JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, buf,
[this, current_height](const char* data, size_t size)
[this, start_height, current_height](const char* data, size_t size)
{
if (parse_block_headers_range(data, size) == BLOCK_HEADERS_REQUIRED) {
if (parse_block_headers_range(data, size) == current_height - start_height) {
update_median_timestamp();
if (m_serversStarted.exchange(1) == 0) {
m_ZMQReader = new ZMQReader(m_params->m_host.c_str(), m_params->m_zmqPort, this);
Expand All @@ -617,14 +620,14 @@ void p2pool::download_block_headers(uint64_t current_height)
}
}
else {
LOGERR(1, "fatal error: couldn't download block headers for heights " << current_height - BLOCK_HEADERS_REQUIRED << " - " << current_height - 1);
LOGERR(1, "fatal error: couldn't download block headers for heights " << start_height << " - " << current_height - 1);
panic();
}
},
[current_height](const char* data, size_t size)
[start_height, current_height](const char* data, size_t size)
{
if (size > 0) {
LOGERR(1, "fatal error: couldn't download block headers for heights " << current_height - BLOCK_HEADERS_REQUIRED << " - " << current_height - 1 << ", error " << log::const_buf(data, size));
LOGERR(1, "fatal error: couldn't download block headers for heights " << start_height << " - " << current_height - 1 << ", error " << log::const_buf(data, size));
panic();
}
});
Expand Down
20 changes: 17 additions & 3 deletions src/pool_block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,12 @@ void PoolBlock::serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, c

for (TxOutput& output : m_outputs) {
writeVarint(output.m_reward, m_mainChainData);
m_mainChainData.push_back(TXOUT_TO_KEY);
m_mainChainData.push_back(output.m_txType);
m_mainChainData.insert(m_mainChainData.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);

if (output.m_txType == TXOUT_TO_TAGGED_KEY) {
m_mainChainData.push_back(output.m_viewTag);
}
}

m_mainChainOutputsBlobSize = static_cast<int>(m_mainChainData.size()) - m_mainChainOutputsOffset;
Expand Down Expand Up @@ -301,9 +305,19 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const
uint64_t PoolBlock::get_payout(const Wallet& w) const
{
for (size_t i = 0, n = m_outputs.size(); i < n; ++i) {
const TxOutput& out = m_outputs[i];
hash eph_public_key;
if ((w.get_eph_public_key(m_txkeySec, i, eph_public_key)) && (eph_public_key == m_outputs[i].m_ephPublicKey)) {
return m_outputs[i].m_reward;

if (out.m_txType == TXOUT_TO_TAGGED_KEY) {
if (w.get_eph_public_key_with_view_tag(m_txkeySec, i, eph_public_key, out.m_viewTag) && (eph_public_key == out.m_ephPublicKey)) {
return out.m_reward;
}
}
else {
uint8_t view_tag;
if (w.get_eph_public_key(m_txkeySec, i, eph_public_key, view_tag) && (eph_public_key == out.m_ephPublicKey)) {
return out.m_reward;
}
}
}

Expand Down
10 changes: 8 additions & 2 deletions src/pool_block.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,13 @@ struct PoolBlock

struct TxOutput
{
FORCEINLINE TxOutput() : m_reward(0), m_ephPublicKey() {}
FORCEINLINE TxOutput(uint64_t r, const hash& k) : m_reward(r), m_ephPublicKey(k) {}
FORCEINLINE TxOutput() : m_reward(0), m_ephPublicKey(), m_txType(0), m_viewTag(0) {}
FORCEINLINE TxOutput(uint64_t r, const hash& k, uint8_t tx_type, uint8_t view_tag) : m_reward(r), m_ephPublicKey(k), m_txType(tx_type), m_viewTag(view_tag) {}

uint64_t m_reward;
hash m_ephPublicKey;
uint8_t m_txType;
uint8_t m_viewTag;
};

std::vector<TxOutput> m_outputs;
Expand Down Expand Up @@ -139,6 +141,10 @@ struct PoolBlock
bool get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash);

uint64_t get_payout(const Wallet& w) const;

// Both tx types are allowed by Monero consensus during v15 because it needs to process pre-fork mempool transactions,
// but P2Pool can switch to using only TXOUT_TO_TAGGED_KEY for miner payouts starting from v15
FORCEINLINE uint8_t get_tx_type() const { return (m_majorVersion < HARDFORK_VIEW_TAGS_VERSION) ? TXOUT_TO_KEY : TXOUT_TO_TAGGED_KEY; }
};

} // namespace p2pool
12 changes: 10 additions & 2 deletions src/pool_block_parser.inl
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
if (num_outputs > 0) {
// Outputs are in the buffer, just read them
// Each output is at least 34 bytes, exit early if there's not enough data left
// 1 byte for reward, 1 byte for TXOUT_TO_KEY, 32 bytes for eph_pub_key
// 1 byte for reward, 1 byte for tx_type, 32 bytes for eph_pub_key
constexpr uint64_t MIN_OUTPUT_SIZE = 34;

if (num_outputs > std::numeric_limits<uint64_t>::max() / MIN_OUTPUT_SIZE) return __LINE__;
Expand All @@ -130,15 +130,23 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
m_outputs.clear();
m_outputs.reserve(num_outputs);

const uint8_t expected_tx_type = get_tx_type();

for (uint64_t i = 0; i < num_outputs; ++i) {
TxOutput t;

READ_VARINT(t.m_reward);
total_reward += t.m_reward;

EXPECT_BYTE(TXOUT_TO_KEY);
EXPECT_BYTE(expected_tx_type);
t.m_txType = expected_tx_type;

READ_BUF(t.m_ephPublicKey.h, HASH_SIZE);

if (expected_tx_type == TXOUT_TO_TAGGED_KEY) {
READ_BYTE(t.m_viewTag);
}

m_outputs.emplace_back(std::move(t));
}

Expand Down
Loading

0 comments on commit 67e3836

Please sign in to comment.