mirror of
https://github.com/SChernykh/p2pool.git
synced 2025-02-02 03:06:27 +00:00
Speed up calc_miner_tx_hash
a lot
Cache keccak state and update it for new extra_nonce in O(1) time instead of O(N). It reduces the number `keccakf` calls from 30-150 to just 2-3. Time to generate 10,000 hashing blobs is reduced from 0.75s to 0.03s.
This commit is contained in:
parent
632f3faac5
commit
a525f34fec
2 changed files with 58 additions and 6 deletions
|
@ -59,6 +59,8 @@ BlockTemplate::BlockTemplate(SideChain* sidechain, RandomX_Hasher_Base* hasher)
|
|||
, m_txkeySec{}
|
||||
, m_poolBlockTemplate(new PoolBlock())
|
||||
, m_finalReward(0)
|
||||
, m_minerTxKeccakState{}
|
||||
, m_minerTxKeccakStateInputLength(0)
|
||||
, m_rng(RandomDeviceSeed::instance)
|
||||
{
|
||||
// Diffuse the initial state in case it has low quality
|
||||
|
@ -136,6 +138,9 @@ BlockTemplate& BlockTemplate::operator=(const BlockTemplate& b)
|
|||
*m_poolBlockTemplate = *b.m_poolBlockTemplate;
|
||||
m_finalReward = b.m_finalReward;
|
||||
|
||||
memcpy(m_minerTxKeccakState, b.m_minerTxKeccakState, sizeof(m_minerTxKeccakState));
|
||||
m_minerTxKeccakStateInputLength = b.m_minerTxKeccakStateInputLength;
|
||||
|
||||
m_minerTx.clear();
|
||||
m_blockHeader.clear();
|
||||
m_minerTxExtra.clear();
|
||||
|
@ -608,6 +613,22 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
|||
}
|
||||
#endif
|
||||
|
||||
memset(m_minerTxKeccakState, 0, sizeof(m_minerTxKeccakState));
|
||||
|
||||
const size_t extra_nonce_offset = m_extraNonceOffsetInTemplate - m_minerTxOffsetInTemplate;
|
||||
if (extra_nonce_offset >= KeccakParams::HASH_DATA_AREA) {
|
||||
// Miner transaction is big enough to cache keccak state up to extra_nonce
|
||||
m_minerTxKeccakStateInputLength = (extra_nonce_offset / KeccakParams::HASH_DATA_AREA) * KeccakParams::HASH_DATA_AREA;
|
||||
|
||||
const uint8_t* in = m_blockTemplateBlob.data() + m_minerTxOffsetInTemplate;
|
||||
int inlen = static_cast<int>(m_minerTxKeccakStateInputLength);
|
||||
|
||||
keccak_step(in, inlen, m_minerTxKeccakState);
|
||||
}
|
||||
else {
|
||||
m_minerTxKeccakStateInputLength = 0;
|
||||
}
|
||||
|
||||
const hash minerTx_hash = calc_miner_tx_hash(0);
|
||||
|
||||
memcpy(m_transactionHashes.data(), minerTx_hash.h, HASH_SIZE);
|
||||
|
@ -879,7 +900,7 @@ hash BlockTemplate::calc_miner_tx_hash(uint32_t extra_nonce) const
|
|||
|
||||
const uint8_t* data = m_blockTemplateBlob.data() + m_minerTxOffsetInTemplate;
|
||||
|
||||
const int extra_nonce_offset = static_cast<int>(m_extraNonceOffsetInTemplate - m_minerTxOffsetInTemplate);
|
||||
const size_t extra_nonce_offset = m_extraNonceOffsetInTemplate - m_minerTxOffsetInTemplate;
|
||||
const uint8_t extra_nonce_buf[EXTRA_NONCE_SIZE] = {
|
||||
static_cast<uint8_t>(extra_nonce >> 0),
|
||||
static_cast<uint8_t>(extra_nonce >> 8),
|
||||
|
@ -889,15 +910,43 @@ hash BlockTemplate::calc_miner_tx_hash(uint32_t extra_nonce) const
|
|||
|
||||
// 1. Prefix (everything except vin_rct_type byte in the end)
|
||||
// Apply extra_nonce in-place because we can't write to the block template here
|
||||
keccak_custom([data, extra_nonce_offset, &extra_nonce_buf](int offset)
|
||||
{
|
||||
const uint32_t k = static_cast<uint32_t>(offset - extra_nonce_offset);
|
||||
const size_t tx_size = m_minerTxSize - 1;
|
||||
|
||||
uint8_t tx_buf[288];
|
||||
if ((m_minerTxKeccakStateInputLength <= extra_nonce_offset) && (m_minerTxKeccakStateInputLength < tx_size) && (tx_size - m_minerTxKeccakStateInputLength <= sizeof(tx_buf))) {
|
||||
const int inlen = static_cast<int>(tx_size - m_minerTxKeccakStateInputLength);
|
||||
memcpy(tx_buf, data + m_minerTxKeccakStateInputLength, inlen);
|
||||
memcpy(tx_buf + extra_nonce_offset - m_minerTxKeccakStateInputLength, extra_nonce_buf, EXTRA_NONCE_SIZE);
|
||||
|
||||
uint64_t st[25];
|
||||
memcpy(st, m_minerTxKeccakState, sizeof(st));
|
||||
keccak_finish(tx_buf, inlen, st);
|
||||
memcpy(hashes, st, HASH_SIZE);
|
||||
|
||||
#if POOL_BLOCK_DEBUG
|
||||
hash check;
|
||||
keccak_custom([data, extra_nonce_offset, &extra_nonce_buf](int offset) {
|
||||
const uint32_t k = static_cast<uint32_t>(offset - static_cast<int>(extra_nonce_offset));
|
||||
if (k < EXTRA_NONCE_SIZE) {
|
||||
return extra_nonce_buf[k];
|
||||
}
|
||||
return data[offset];
|
||||
},
|
||||
static_cast<int>(m_minerTxSize) - 1, hashes, HASH_SIZE);
|
||||
}, static_cast<int>(tx_size), check.h, HASH_SIZE);
|
||||
|
||||
if (memcmp(hashes, check.h, HASH_SIZE) != 0) {
|
||||
LOGERR(1, "calc_miner_tx_hash fast path is broken. Fix the code!");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
keccak_custom([data, extra_nonce_offset, &extra_nonce_buf](int offset) {
|
||||
const uint32_t k = static_cast<uint32_t>(offset - static_cast<int>(extra_nonce_offset));
|
||||
if (k < EXTRA_NONCE_SIZE) {
|
||||
return extra_nonce_buf[k];
|
||||
}
|
||||
return data[offset];
|
||||
}, static_cast<int>(tx_size), hashes, HASH_SIZE);
|
||||
}
|
||||
|
||||
// 2. Base RCT, single 0 byte in miner tx
|
||||
static constexpr uint8_t known_second_hash[HASH_SIZE] = {
|
||||
|
|
|
@ -103,6 +103,9 @@ private:
|
|||
|
||||
// Temp vectors, will be cleaned up after use and skipped in copy constructor/assignment operators
|
||||
std::vector<uint8_t> m_minerTx;
|
||||
uint64_t m_minerTxKeccakState[25];
|
||||
size_t m_minerTxKeccakStateInputLength;
|
||||
|
||||
std::vector<uint8_t> m_blockHeader;
|
||||
std::vector<uint8_t> m_minerTxExtra;
|
||||
std::vector<uint8_t> m_transactionHashes;
|
||||
|
|
Loading…
Reference in a new issue