KawPow WIP

This commit is contained in:
SChernykh 2020-05-24 23:57:41 +02:00
parent 07025dc41b
commit 22b937cc1c
88 changed files with 11004 additions and 8383 deletions

View file

@ -0,0 +1,153 @@
/* XMRig
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
* Copyright 2017-2019 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
* Copyright 2018-2019 tevador <tevador@gmail.com>
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2019 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 <cinttypes>
#include "3rdparty/libethash/ethash.h"
#include "3rdparty/libethash/ethash_internal.h"
#include "3rdparty/libethash/data_sizes.h"
#include "base/io/log/Log.h"
#include "base/tools/Chrono.h"
#include "crypto/common/VirtualMemory.h"
#include "crypto/kawpow/KPCache.h"
namespace xmrig {
std::mutex KPCache::s_cacheMutex;
KPCache KPCache::s_cache;
KPCache::KPCache()
{
}
KPCache::~KPCache()
{
delete m_memory;
}
bool KPCache::init(uint32_t epoch)
{
if (epoch >= sizeof(cache_sizes) / sizeof(cache_sizes[0])) {
return false;
}
if (m_epoch == epoch) {
return true;
}
const uint64_t start_ms = Chrono::steadyMSecs();
const size_t size = cache_sizes[epoch];
if (!m_memory || m_memory->size() < size) {
delete m_memory;
m_memory = new VirtualMemory(size, false, false, false);
}
const ethash_h256_t seedhash = ethash_get_seedhash(epoch);
ethash_compute_cache_nodes(m_memory->raw(), size, &seedhash);
ethash_light cache;
cache.cache = m_memory->raw();
cache.cache_size = size;
cache.num_parent_nodes = cache.cache_size / sizeof(node);
calculate_fast_mod_data(cache.num_parent_nodes, cache.reciprocal, cache.increment, cache.shift);
for (uint32_t i = 0; i < sizeof(m_l1Cache) / sizeof(node); ++i) {
ethash_calculate_dag_item_opt(((node*)m_l1Cache) + i, i, num_dataset_parents, &cache);
}
m_size = size;
m_epoch = epoch;
LOG_INFO("KawPow light cache for epoch %u calculated (%" PRIu64 " ms)", epoch, Chrono::steadyMSecs() - start_ms);
return true;
}
void* KPCache::data() const
{
return m_memory ? m_memory->raw() : nullptr;
}
static inline uint32_t clz(uint32_t a)
{
#ifdef _MSC_VER
unsigned long index;
_BitScanReverse(&index, a);
return 31 - index;
#else
return __builtin_clz(a);
#endif
}
uint64_t KPCache::dag_size(uint32_t epoch)
{
if (epoch >= sizeof(dag_sizes) / sizeof(dag_sizes[0])) {
return 0;
}
return dag_sizes[epoch];
}
void KPCache::calculate_fast_mod_data(uint32_t divisor, uint32_t& reciprocal, uint32_t& increment, uint32_t& shift)
{
if ((divisor & (divisor - 1)) == 0) {
reciprocal = 1;
increment = 0;
shift = 31U - clz(divisor);
}
else {
shift = 63U - clz(divisor);
const uint64_t N = 1ULL << shift;
const uint64_t q = N / divisor;
const uint64_t r = N - q * divisor;
if (r * 2 < divisor)
{
reciprocal = static_cast<uint32_t>(q);
increment = 1;
}
else
{
reciprocal = static_cast<uint32_t>(q + 1);
increment = 0;
}
}
}
} // namespace xmrig

View file

@ -0,0 +1,80 @@
/* XMRig
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
* Copyright 2017-2019 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
* Copyright 2018-2019 tevador <tevador@gmail.com>
* Copyright 2018-2020 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2019 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_KP_CACHE_H
#define XMRIG_KP_CACHE_H
#include "base/tools/Object.h"
#include <mutex>
namespace xmrig
{
class VirtualMemory;
class KPCache
{
public:
static constexpr size_t l1_cache_size = 16 * 1024;
static constexpr size_t l1_cache_num_items = l1_cache_size / sizeof(uint32_t);
static constexpr uint32_t num_dataset_parents = 512;
XMRIG_DISABLE_COPY_MOVE(KPCache)
KPCache();
~KPCache();
bool init(uint32_t epoch);
void* data() const;
size_t size() const { return m_size; }
uint32_t epoch() const { return m_epoch; }
const uint32_t* l1_cache() const { return m_l1Cache; }
static uint64_t dag_size(uint32_t epoch);
static void calculate_fast_mod_data(uint32_t divisor, uint32_t &reciprocal, uint32_t &increment, uint32_t& shift);
static std::mutex s_cacheMutex;
static KPCache s_cache;
private:
VirtualMemory* m_memory = nullptr;
size_t m_size = 0;
uint32_t m_epoch = 0xFFFFFFFFUL;
uint32_t m_l1Cache[l1_cache_num_items] = {};
};
} /* namespace xmrig */
#endif /* XMRIG_KP_CACHE_H */

View file

@ -0,0 +1,353 @@
/* XMRig
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
* Copyright 2017-2019 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
* Copyright 2018-2019 tevador <tevador@gmail.com>
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2019 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 "crypto/kawpow/KPHash.h"
#include "crypto/kawpow/KPCache.h"
#include "3rdparty/libethash/ethash.h"
#include "3rdparty/libethash/ethash_internal.h"
#include "3rdparty/libethash/data_sizes.h"
#ifdef _MSC_VER
#include <intrin.h>
#endif
namespace xmrig {
static const uint32_t ravencoin_kawpow[15] = {
0x00000072, //R
0x00000041, //A
0x00000056, //V
0x00000045, //E
0x0000004E, //N
0x00000043, //C
0x0000004F, //O
0x00000049, //I
0x0000004E, //N
0x0000004B, //K
0x00000041, //A
0x00000057, //W
0x00000050, //P
0x0000004F, //O
0x00000057, //W
};
static const uint32_t fnv_prime = 0x01000193;
static const uint32_t fnv_offset_basis = 0x811c9dc5;
static inline uint32_t fnv1a(uint32_t u, uint32_t v)
{
return (u ^ v) * fnv_prime;
}
static inline uint32_t kiss99(uint32_t& z, uint32_t& w, uint32_t& jsr, uint32_t& jcong)
{
z = 36969 * (z & 0xffff) + (z >> 16);
w = 18000 * (w & 0xffff) + (w >> 16);
jcong = 69069 * jcong + 1234567;
jsr ^= (jsr << 17);
jsr ^= (jsr >> 13);
jsr ^= (jsr << 5);
return (((z << 16) + w) ^ jcong) + jsr;
}
static inline uint32_t rotl(uint32_t n, uint32_t c)
{
#ifdef _MSC_VER
return _rotl(n, c);
#else
c &= 31;
uint32_t neg_c = (uint32_t)(-(int32_t)c);
return (n << c) | (n >> (neg_c & 31));
#endif
}
static inline uint32_t rotr(uint32_t n, uint32_t c)
{
#ifdef _MSC_VER
return _rotr(n, c);
#else
c &= 31;
uint32_t neg_c = (uint32_t)(-(int32_t)c);
return (n >> c) | (n << (neg_c & 31));
#endif
}
static inline void random_merge(uint32_t& a, uint32_t b, uint32_t selector)
{
const uint32_t x = (selector >> 16) % 31 + 1;
switch (selector % 4)
{
case 0:
a = (a * 33) + b;
break;
case 1:
a = (a ^ b) * 33;
break;
case 2:
a = rotl(a, x) ^ b;
break;
case 3:
a = rotr(a, x) ^ b;
break;
default:
#ifdef _MSC_VER
__assume(false);
#else
__builtin_unreachable();
#endif
break;
}
}
static inline uint32_t clz(uint32_t a)
{
#ifdef _MSC_VER
unsigned long index;
_BitScanReverse(&index, a);
return a ? (31 - index) : 32;
#else
return a ? (uint32_t)__builtin_clz(a) : 32;
#endif
}
static inline uint32_t popcount(uint32_t a)
{
#ifdef _MSC_VER
return __popcnt(a);
#else
return __builtin_popcount(a);
#endif
}
static inline uint32_t random_math(uint32_t a, uint32_t b, uint32_t selector)
{
switch (selector % 11)
{
case 0:
return a + b;
case 1:
return a * b;
case 2:
return (uint64_t(a) * b) >> 32;
case 3:
return (a < b) ? a : b;
case 4:
return rotl(a, b);
case 5:
return rotr(a, b);
case 6:
return a & b;
case 7:
return a | b;
case 8:
return a ^ b;
case 9:
return clz(a) + clz(b);
case 10:
return popcount(a) + popcount(b);
default:
#ifdef _MSC_VER
__assume(false);
#else
__builtin_unreachable();
#endif
break;
}
}
void KPHash::calculate(const KPCache& light_cache, uint32_t block_height, const uint8_t (&header_hash)[32], uint64_t nonce, uint32_t (&output)[8], uint32_t (&mix_hash)[8])
{
uint32_t keccak_state[25];
uint32_t mix[LANES][REGS];
memcpy(keccak_state, header_hash, sizeof(header_hash));
memcpy(keccak_state + 8, &nonce, sizeof(nonce));
memcpy(keccak_state + 10, ravencoin_kawpow, sizeof(ravencoin_kawpow));
ethash_keccakf800(keccak_state);
uint32_t z = fnv1a(fnv_offset_basis, keccak_state[0]);
uint32_t w = fnv1a(z, keccak_state[1]);
uint32_t jsr, jcong;
for (uint32_t l = 0; l < LANES; ++l) {
uint32_t z1 = z;
uint32_t w1 = w;
jsr = fnv1a(w, l);
jcong = fnv1a(jsr, l);
for (uint32_t r = 0; r < REGS; ++r) {
mix[l][r] = kiss99(z1, w1, jsr, jcong);
}
}
const uint32_t prog_number = block_height / PERIOD_LENGTH;
uint32_t dst_seq[REGS];
uint32_t src_seq[REGS];
z = fnv1a(fnv_offset_basis, prog_number);
w = fnv1a(z, 0);
jsr = fnv1a(w, prog_number);
jcong = fnv1a(jsr, 0);
for (uint32_t i = 0; i < REGS; ++i)
{
dst_seq[i] = i;
src_seq[i] = i;
}
for (uint32_t i = REGS; i > 1; --i)
{
std::swap(dst_seq[i - 1], dst_seq[kiss99(z, w, jsr, jcong) % i]);
std::swap(src_seq[i - 1], src_seq[kiss99(z, w, jsr, jcong) % i]);
}
const uint32_t epoch = light_cache.epoch();
const uint32_t num_items = static_cast<uint32_t>(dag_sizes[epoch] / ETHASH_MIX_BYTES / 2);
constexpr size_t num_words_per_lane = 256 / (sizeof(uint32_t) * LANES);
constexpr int max_operations = (CNT_CACHE > CNT_MATH) ? CNT_CACHE : CNT_MATH;
ethash_light cache;
cache.cache = light_cache.data();
cache.cache_size = light_cache.size();
cache.block_number = block_height;
cache.num_parent_nodes = cache.cache_size / sizeof(node);
KPCache::calculate_fast_mod_data(cache.num_parent_nodes, cache.reciprocal, cache.increment, cache.shift);
uint32_t z0 = z;
uint32_t w0 = w;
uint32_t jsr0 = jsr;
uint32_t jcong0 = jcong;
for (uint32_t r = 0; r < ETHASH_ACCESSES; ++r) {
uint32_t item_index = (mix[r % LANES][0] % num_items) * 4;
node item[4];
ethash_calculate_dag_item_opt(item + 0, item_index + 0, KPCache::num_dataset_parents, &cache);
ethash_calculate_dag_item_opt(item + 1, item_index + 1, KPCache::num_dataset_parents, &cache);
ethash_calculate_dag_item_opt(item + 2, item_index + 2, KPCache::num_dataset_parents, &cache);
ethash_calculate_dag_item_opt(item + 3, item_index + 3, KPCache::num_dataset_parents, &cache);
uint32_t dst_counter = 0;
uint32_t src_counter = 0;
z = z0;
w = w0;
jsr = jsr0;
jcong = jcong0;
for (uint32_t i = 0; i < max_operations; ++i) {
if (i < CNT_CACHE) {
const uint32_t src = src_seq[(src_counter++) % REGS];
const uint32_t dst = dst_seq[(dst_counter++) % REGS];
const uint32_t sel = kiss99(z, w, jsr, jcong);
for (uint32_t j = 0; j < LANES; ++j) {
random_merge(mix[j][dst], light_cache.l1_cache()[mix[j][src] % KPCache::l1_cache_num_items], sel);
}
}
if (i < CNT_MATH)
{
const uint32_t src_rnd = kiss99(z, w, jsr, jcong) % (REGS * (REGS - 1));
const uint32_t src1 = src_rnd % REGS;
uint32_t src2 = src_rnd / REGS;
if (src2 >= src1) {
++src2;
}
const uint32_t sel1 = kiss99(z, w, jsr, jcong);
const uint32_t dst = dst_seq[(dst_counter++) % REGS];
const uint32_t sel2 = kiss99(z, w, jsr, jcong);
for (size_t l = 0; l < LANES; ++l)
{
const uint32_t data = random_math(mix[l][src1], mix[l][src2], sel1);
random_merge(mix[l][dst], data, sel2);
}
}
}
uint32_t dsts[num_words_per_lane];
uint32_t sels[num_words_per_lane];
for (uint32_t i = 0; i < num_words_per_lane; ++i) {
dsts[i] = (i == 0) ? 0 : dst_seq[(dst_counter++) % REGS];
sels[i] = kiss99(z, w, jsr, jcong);
}
for (uint32_t l = 0; l < LANES; ++l) {
const uint32_t offset = ((l ^ r) % LANES) * num_words_per_lane;
for (size_t i = 0; i < num_words_per_lane; ++i) {
random_merge(mix[l][dsts[i]], ((uint32_t*)item)[offset + i], sels[i]);
}
}
}
uint32_t lane_hash[LANES];
for (uint32_t l = 0; l < LANES; ++l)
{
lane_hash[l] = fnv_offset_basis;
for (uint32_t i = 0; i < REGS; ++i) {
lane_hash[l] = fnv1a(lane_hash[l], mix[l][i]);
}
}
constexpr uint32_t num_words = 8;
for (uint32_t i = 0; i < num_words; ++i) {
mix_hash[i] = fnv_offset_basis;
}
for (uint32_t l = 0; l < LANES; ++l)
mix_hash[l % num_words] = fnv1a(mix_hash[l % num_words], lane_hash[l]);
memcpy(keccak_state + 8, mix_hash, sizeof(mix_hash));
memcpy(keccak_state + 16, ravencoin_kawpow, sizeof(uint32_t) * 9);
ethash_keccakf800(keccak_state);
memcpy(output, keccak_state, sizeof(output));
}
} // namespace xmrig

View file

@ -0,0 +1,58 @@
/* XMRig
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
* Copyright 2017-2019 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
* Copyright 2018-2019 tevador <tevador@gmail.com>
* Copyright 2018-2020 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2019 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_KP_HASH_H
#define XMRIG_KP_HASH_H
#include <stdint.h>
namespace xmrig
{
class KPCache;
class KPHash
{
public:
static constexpr uint32_t EPOCH_LENGTH = 7500;
static constexpr uint32_t PERIOD_LENGTH = 3;
static constexpr int CNT_CACHE = 11;
static constexpr int CNT_MATH = 18;
static constexpr uint32_t REGS = 32;
static constexpr uint32_t LANES = 16;
static void calculate(const KPCache& light_cache, uint32_t block_height, const uint8_t (&header_hash)[32], uint64_t nonce, uint32_t (&output)[8], uint32_t (&mix_hash)[8]);
};
} /* namespace xmrig */
#endif /* XMRIG_KP_HASH_H */