diff --git a/CMakeLists.txt b/CMakeLists.txt index 37e235ca5..967f5007f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ option(WITH_ARGON2 "Enable Argon2 algorithms family" ON) option(WITH_ASTROBWT "Enable AstroBWT algorithms family" ON) option(WITH_KAWPOW "Enable KawPow algorithms family" ON) option(WITH_HTTP "Enable HTTP protocol support (client/server)" ON) -option(WITH_DEBUG_LOG "Enable debug log output" OFF) +option(WITH_DEBUG_LOG "Enable debug log output" ON) option(WITH_TLS "Enable OpenSSL support" ON) option(WITH_ASM "Enable ASM PoW implementations" ON) option(WITH_MSR "Enable MSR mod & 1st-gen Ryzen fix" ON) @@ -82,6 +82,7 @@ set(HEADERS_CRYPTO src/crypto/cn/hash.h src/crypto/cn/skein_port.h src/crypto/cn/soft_aes.h + src/crypto/cn/umul128.h src/crypto/common/HugePagesInfo.h src/crypto/common/MemoryPool.h src/crypto/common/Nonce.h diff --git a/cmake/os.cmake b/cmake/os.cmake index 099311030..6516c1f7a 100644 --- a/cmake/os.cmake +++ b/cmake/os.cmake @@ -1,3 +1,7 @@ +if (CMAKE_SIZEOF_VOID_P EQUAL 8) + add_definitions(/DXMRIG_64_BIT) +endif() + if (WIN32) set(XMRIG_OS_WIN ON) elseif (APPLE) diff --git a/src/base/base.cmake b/src/base/base.cmake index 8c48fe752..c947f4b13 100644 --- a/src/base/base.cmake +++ b/src/base/base.cmake @@ -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 ) diff --git a/src/base/tools/cryptonote/BlobReader.h b/src/base/tools/cryptonote/BlobReader.h new file mode 100644 index 000000000..90815299b --- /dev/null +++ b/src/base/tools/cryptonote/BlobReader.h @@ -0,0 +1,101 @@ +/* XMRig + * Copyright 2014-2021 The Monero Project + * Copyright 2018-2021 SChernykh + * Copyright 2016-2021 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_BLOBREADER_H +#define XMRIG_BLOBREADER_H + + +#include + + +namespace xmrig { + + +class CBlobReader +{ +public: + inline CBlobReader(const void* data, size_t size) + : m_data(reinterpret_cast(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 + inline bool operator()(uint8_t(&data)[N]) + { + for (size_t i = 0; i < N; ++i) { + if (!getByte(data[i])) { + return false; + } + } + return true; + } + + template + 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(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 */ diff --git a/src/base/tools/cryptonote/BlockTemplate.cpp b/src/base/tools/cryptonote/BlockTemplate.cpp new file mode 100644 index 000000000..8b7845ab4 --- /dev/null +++ b/src/base/tools/cryptonote/BlockTemplate.cpp @@ -0,0 +1,215 @@ +/* XMRig + * Copyright 2014-2021 The Monero Project + * Copyright 2018-2021 SChernykh + * Copyright 2016-2021 XMRig , + * + * 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 . + */ + + +#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(k) & 0x7F) | 0x80); + k >>= 7; + } + hashingBlob.emplace_back(static_cast(k)); + + for (int i = 0; i < hashingBlob.size(); ++i) + printf("%02x", hashingBlob[i]); +} + + +} /* namespace xmrig */ diff --git a/src/base/tools/cryptonote/BlockTemplate.h b/src/base/tools/cryptonote/BlockTemplate.h new file mode 100644 index 000000000..5c242395f --- /dev/null +++ b/src/base/tools/cryptonote/BlockTemplate.h @@ -0,0 +1,86 @@ +/* XMRig + * Copyright 2014-2021 The Monero Project + * Copyright 2018-2021 SChernykh + * Copyright 2016-2021 XMRig , + * + * 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 . + */ + +#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 */ diff --git a/src/base/tools/cryptonote/WalletAddress.cpp b/src/base/tools/cryptonote/WalletAddress.cpp new file mode 100644 index 000000000..2ee2e7a38 --- /dev/null +++ b/src/base/tools/cryptonote/WalletAddress.cpp @@ -0,0 +1,116 @@ +/* XMRig + * Copyright 2014-2021 The Monero Project + * Copyright 2018-2021 SChernykh + * Copyright 2016-2021 XMRig , + * + * 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 . + */ + + +#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 + + +#pragma optimize("", off) + + +namespace xmrig { + + +bool WalletAddress::Decode(const String& address) +{ + static constexpr std::array 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(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(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(digit), &hi); + if ((tmp < num) || hi) { + return false; + } + + num = tmp; + order *= alphabet_size; + } + + address_data += block_sizes.back(); + + uint8_t* p = reinterpret_cast(&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 */ diff --git a/src/base/tools/cryptonote/WalletAddress.h b/src/base/tools/cryptonote/WalletAddress.h new file mode 100644 index 000000000..166b99c24 --- /dev/null +++ b/src/base/tools/cryptonote/WalletAddress.h @@ -0,0 +1,44 @@ +/* XMRig + * Copyright 2014-2021 The Monero Project + * Copyright 2018-2021 SChernykh + * Copyright 2016-2021 XMRig , + * + * 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 . + */ + +#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 */ diff --git a/src/crypto/cn/CnHash.cpp b/src/crypto/cn/CnHash.cpp index f6a77f6a7..e9232f0b4 100644 --- a/src/crypto/cn/CnHash.cpp +++ b/src/crypto/cn/CnHash.cpp @@ -31,6 +31,8 @@ #include "crypto/common/VirtualMemory.h" +#include "crypto/cn/umul128.h" + #if defined(XMRIG_ARM) # include "crypto/cn/CryptoNight_arm.h" #else diff --git a/src/crypto/cn/CryptoNight_arm.h b/src/crypto/cn/CryptoNight_arm.h index 227c565eb..443f06ec9 100644 --- a/src/crypto/cn/CryptoNight_arm.h +++ b/src/crypto/cn/CryptoNight_arm.h @@ -67,41 +67,6 @@ static inline void do_skein_hash(const uint8_t *input, size_t len, uint8_t *outp void (* const extra_hashes[4])(const uint8_t *, size_t, uint8_t *) = {do_blake_hash, do_groestl_hash, do_jh_hash, do_skein_hash}; -#if defined (__arm64__) || defined (__aarch64__) -static inline uint64_t __umul128(uint64_t a, uint64_t b, uint64_t* hi) -{ - unsigned __int128 r = (unsigned __int128) a * (unsigned __int128) b; - *hi = r >> 64; - return (uint64_t) r; -} -#else -static inline uint64_t __umul128(uint64_t multiplier, uint64_t multiplicand, uint64_t *product_hi) { - // multiplier = ab = a * 2^32 + b - // multiplicand = cd = c * 2^32 + d - // ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d - uint64_t a = multiplier >> 32; - uint64_t b = multiplier & 0xFFFFFFFF; - uint64_t c = multiplicand >> 32; - uint64_t d = multiplicand & 0xFFFFFFFF; - - //uint64_t ac = a * c; - uint64_t ad = a * d; - //uint64_t bc = b * c; - uint64_t bd = b * d; - - uint64_t adbc = ad + (b * c); - uint64_t adbc_carry = adbc < ad ? 1 : 0; - - // multiplier * multiplicand = product_hi * 2^64 + product_lo - uint64_t product_lo = bd + (adbc << 32); - uint64_t product_lo_carry = product_lo < bd ? 1 : 0; - *product_hi = (a * c) + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry; - - return product_lo; -} -#endif - - // This will shift and xor tmp1 into itself as 4 32-bit vals such as // sl_xor(a1 a2 a3 a4) = a1 (a2^a1) (a3^a2^a1) (a4^a3^a2^a1) static inline __m128i sl_xor(__m128i tmp1) diff --git a/src/crypto/cn/CryptoNight_x86.h b/src/crypto/cn/CryptoNight_x86.h index cc88342be..e5cfe4525 100644 --- a/src/crypto/cn/CryptoNight_x86.h +++ b/src/crypto/cn/CryptoNight_x86.h @@ -75,18 +75,7 @@ static inline void do_skein_hash(const uint8_t *input, size_t len, uint8_t *outp void (* const extra_hashes[4])(const uint8_t *, size_t, uint8_t *) = {do_blake_hash, do_groestl_hash, do_jh_hash, do_skein_hash}; -#if defined(__x86_64__) || defined(_M_AMD64) -# ifdef __GNUC__ -static inline uint64_t __umul128(uint64_t a, uint64_t b, uint64_t* hi) -{ - unsigned __int128 r = (unsigned __int128) a * (unsigned __int128) b; - *hi = r >> 64; - return (uint64_t) r; -} -# else - #define __umul128 _umul128 -# endif -#elif defined(__i386__) || defined(_M_IX86) +#if defined(__i386__) || defined(_M_IX86) static inline int64_t _mm_cvtsi128_si64(__m128i a) { return ((uint64_t)(uint32_t)_mm_cvtsi128_si32(a) | ((uint64_t)(uint32_t)_mm_cvtsi128_si32(_mm_srli_si128(a, 4)) << 32)); @@ -95,31 +84,6 @@ static inline int64_t _mm_cvtsi128_si64(__m128i a) static inline __m128i _mm_cvtsi64_si128(int64_t a) { return _mm_set_epi64x(0, a); } - -static inline uint64_t __umul128(uint64_t multiplier, uint64_t multiplicand, uint64_t *product_hi) { - // multiplier = ab = a * 2^32 + b - // multiplicand = cd = c * 2^32 + d - // ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d - uint64_t a = multiplier >> 32; - uint64_t b = multiplier & 0xFFFFFFFF; - uint64_t c = multiplicand >> 32; - uint64_t d = multiplicand & 0xFFFFFFFF; - - //uint64_t ac = a * c; - uint64_t ad = a * d; - //uint64_t bc = b * c; - uint64_t bd = b * d; - - uint64_t adbc = ad + (b * c); - uint64_t adbc_carry = adbc < ad ? 1 : 0; - - // multiplier * multiplicand = product_hi * 2^64 + product_lo - uint64_t product_lo = bd + (adbc << 32); - uint64_t product_lo_carry = product_lo < bd ? 1 : 0; - *product_hi = (a * c) + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry; - - return product_lo; -} #endif diff --git a/src/crypto/cn/umul128.h b/src/crypto/cn/umul128.h new file mode 100644 index 000000000..a6e77da61 --- /dev/null +++ b/src/crypto/cn/umul128.h @@ -0,0 +1,68 @@ +/* + * 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 + * 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 . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this Program, or any covered work, by linking or combining + * it with OpenSSL (or a modified version of that library), containing parts + * covered by the terms of OpenSSL License and SSLeay License, the licensors + * of this Program grant you additional permission to convey the resulting work. + * + */ + +#pragma once + + +#include + +#ifdef XMRIG_64_BIT +# ifdef _MSC_VER +# include +# pragma intrinsic(_umul128) +# define __umul128 _umul128 +# elif defined __GNUC__ + static inline uint64_t _umul128(uint64_t a, uint64_t b, uint64_t* hi) + { + unsigned __int128 r = (unsigned __int128) a * (unsigned __int128) b; + *hi = r >> 64; + return (uint64_t) r; + } +# define __umul128 _umul128 +# endif +#else +static inline uint64_t __umul128(uint64_t multiplier, uint64_t multiplicand, uint64_t *product_hi) { + // multiplier = ab = a * 2^32 + b + // multiplicand = cd = c * 2^32 + d + // ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d + uint64_t a = multiplier >> 32; + uint64_t b = multiplier & 0xFFFFFFFF; + uint64_t c = multiplicand >> 32; + uint64_t d = multiplicand & 0xFFFFFFFF; + + //uint64_t ac = a * c; + uint64_t ad = a * d; + //uint64_t bc = b * c; + uint64_t bd = b * d; + + uint64_t adbc = ad + (b * c); + uint64_t adbc_carry = adbc < ad ? 1 : 0; + + // multiplier * multiplicand = product_hi * 2^64 + product_lo + uint64_t product_lo = bd + (adbc << 32); + uint64_t product_lo_carry = product_lo < bd ? 1 : 0; + *product_hi = (a * c) + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry; + + return product_lo; +} +#endif