mirror of
https://github.com/xmrig/xmrig.git
synced 2024-11-17 00:07:44 +00:00
Update BlockTemplate class.
This commit is contained in:
parent
a28f411339
commit
bea2a6cf5b
11 changed files with 598 additions and 257 deletions
176
src/3rdparty/epee/span.h
vendored
Normal file
176
src/3rdparty/epee/span.h
vendored
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
// Copyright (c) 2017-2020, The Monero Project
|
||||||
|
//
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without modification, are
|
||||||
|
// permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||||
|
// conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||||
|
// of conditions and the following disclaimer in the documentation and/or other
|
||||||
|
// materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||||
|
// used to endorse or promote products derived from this software without specific
|
||||||
|
// prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||||
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||||
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
/*!
|
||||||
|
\brief Non-owning sequence of data. Does not deep copy
|
||||||
|
|
||||||
|
Inspired by `gsl::span` and/or `boost::iterator_range`. This class is
|
||||||
|
intended to be used as a parameter type for functions that need to take a
|
||||||
|
writable or read-only sequence of data. Most common cases are `span<char>`
|
||||||
|
and `span<std::uint8_t>`. Using as a class member is only recommended if
|
||||||
|
clearly documented as not doing a deep-copy. C-arrays are easily convertible
|
||||||
|
to this type.
|
||||||
|
|
||||||
|
\note Conversion from C string literal to `span<const char>` will include
|
||||||
|
the NULL-terminator.
|
||||||
|
\note Never allows derived-to-base pointer conversion; an array of derived
|
||||||
|
types is not an array of base types.
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
class span
|
||||||
|
{
|
||||||
|
template<typename U>
|
||||||
|
static constexpr bool safe_conversion() noexcept
|
||||||
|
{
|
||||||
|
// Allow exact matches or `T*` -> `const T*`.
|
||||||
|
using with_const = typename std::add_const<U>::type;
|
||||||
|
return std::is_same<T, U>() ||
|
||||||
|
(std::is_const<T>() && std::is_same<T, with_const>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
using value_type = T;
|
||||||
|
using size_type = std::size_t;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using pointer = T*;
|
||||||
|
using const_pointer = const T*;
|
||||||
|
using reference = T&;
|
||||||
|
using const_reference = const T&;
|
||||||
|
using iterator = pointer;
|
||||||
|
using const_iterator = const_pointer;
|
||||||
|
|
||||||
|
constexpr span() noexcept : ptr(nullptr), len(0) {}
|
||||||
|
constexpr span(std::nullptr_t) noexcept : span() {}
|
||||||
|
|
||||||
|
//! Prevent derived-to-base conversions; invalid in this context.
|
||||||
|
template<typename U, typename = typename std::enable_if<safe_conversion<U>()>::type>
|
||||||
|
constexpr span(U* const src_ptr, const std::size_t count) noexcept
|
||||||
|
: ptr(src_ptr), len(count) {}
|
||||||
|
|
||||||
|
//! Conversion from C-array. Prevents common bugs with sizeof + arrays.
|
||||||
|
template<std::size_t N>
|
||||||
|
constexpr span(T (&src)[N]) noexcept : span(src, N) {}
|
||||||
|
|
||||||
|
constexpr span(const span&) noexcept = default;
|
||||||
|
span& operator=(const span&) noexcept = default;
|
||||||
|
|
||||||
|
/*! Try to remove `amount` elements from beginning of span.
|
||||||
|
\return Number of elements removed. */
|
||||||
|
std::size_t remove_prefix(std::size_t amount) noexcept
|
||||||
|
{
|
||||||
|
amount = std::min(len, amount);
|
||||||
|
ptr += amount;
|
||||||
|
len -= amount;
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr iterator begin() const noexcept { return ptr; }
|
||||||
|
constexpr const_iterator cbegin() const noexcept { return ptr; }
|
||||||
|
|
||||||
|
constexpr iterator end() const noexcept { return begin() + size(); }
|
||||||
|
constexpr const_iterator cend() const noexcept { return cbegin() + size(); }
|
||||||
|
|
||||||
|
constexpr bool empty() const noexcept { return size() == 0; }
|
||||||
|
constexpr pointer data() const noexcept { return ptr; }
|
||||||
|
constexpr std::size_t size() const noexcept { return len; }
|
||||||
|
constexpr std::size_t size_bytes() const noexcept { return size() * sizeof(value_type); }
|
||||||
|
|
||||||
|
T &operator[](size_t idx) noexcept { return ptr[idx]; }
|
||||||
|
const T &operator[](size_t idx) const noexcept { return ptr[idx]; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
T* ptr;
|
||||||
|
std::size_t len;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \return `span<const T::value_type>` from a STL compatible `src`.
|
||||||
|
template<typename T>
|
||||||
|
constexpr span<const typename T::value_type> to_span(const T& src)
|
||||||
|
{
|
||||||
|
// compiler provides diagnostic if size() is not size_t.
|
||||||
|
return {src.data(), src.size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \return `span<T::value_type>` from a STL compatible `src`.
|
||||||
|
template<typename T>
|
||||||
|
constexpr span<typename T::value_type> to_mut_span(T& src)
|
||||||
|
{
|
||||||
|
// compiler provides diagnostic if size() is not size_t.
|
||||||
|
return {src.data(), src.size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr bool has_padding() noexcept
|
||||||
|
{
|
||||||
|
return !std::is_standard_layout<T>() || alignof(T) != 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \return Cast data from `src` as `span<const std::uint8_t>`.
|
||||||
|
template<typename T>
|
||||||
|
span<const std::uint8_t> to_byte_span(const span<const T> src) noexcept
|
||||||
|
{
|
||||||
|
static_assert(!has_padding<T>(), "source type may have padding");
|
||||||
|
return {reinterpret_cast<const std::uint8_t*>(src.data()), src.size_bytes()};
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \return `span<const std::uint8_t>` which represents the bytes at `&src`.
|
||||||
|
template<typename T>
|
||||||
|
span<const std::uint8_t> as_byte_span(const T& src) noexcept
|
||||||
|
{
|
||||||
|
static_assert(!std::is_empty<T>(), "empty types will not work -> sizeof == 1");
|
||||||
|
static_assert(!has_padding<T>(), "source type may have padding");
|
||||||
|
return {reinterpret_cast<const std::uint8_t*>(std::addressof(src)), sizeof(T)};
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \return `span<std::uint8_t>` which represents the bytes at `&src`.
|
||||||
|
template<typename T>
|
||||||
|
span<std::uint8_t> as_mut_byte_span(T& src) noexcept
|
||||||
|
{
|
||||||
|
static_assert(!std::is_empty<T>(), "empty types will not work -> sizeof == 1");
|
||||||
|
static_assert(!has_padding<T>(), "source type may have padding");
|
||||||
|
return {reinterpret_cast<std::uint8_t*>(std::addressof(src)), sizeof(T)};
|
||||||
|
}
|
||||||
|
|
||||||
|
//! make a span from a std::string
|
||||||
|
template<typename T>
|
||||||
|
span<const T> strspan(const std::string &s) noexcept
|
||||||
|
{
|
||||||
|
static_assert(std::is_same<T, char>() || std::is_same<T, unsigned char>() || std::is_same<T, int8_t>() || std::is_same<T, uint8_t>(), "Unexpected type");
|
||||||
|
return {reinterpret_cast<const T*>(s.data()), s.size()};
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
set(HEADERS_BASE
|
set(HEADERS_BASE
|
||||||
src/3rdparty/fmt/format.cc
|
src/3rdparty/epee/span.h
|
||||||
src/base/api/interfaces/IApiListener.h
|
src/base/api/interfaces/IApiListener.h
|
||||||
src/base/crypto/Algorithm.h
|
src/base/crypto/Algorithm.h
|
||||||
src/base/crypto/Coin.h
|
src/base/crypto/Coin.h
|
||||||
|
@ -81,11 +81,13 @@ set(HEADERS_BASE
|
||||||
src/base/tools/cryptonote/WalletAddress.h
|
src/base/tools/cryptonote/WalletAddress.h
|
||||||
src/base/tools/Cvt.h
|
src/base/tools/Cvt.h
|
||||||
src/base/tools/Handle.h
|
src/base/tools/Handle.h
|
||||||
|
src/base/tools/Span.h
|
||||||
src/base/tools/String.h
|
src/base/tools/String.h
|
||||||
src/base/tools/Timer.h
|
src/base/tools/Timer.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SOURCES_BASE
|
set(SOURCES_BASE
|
||||||
|
src/3rdparty/fmt/format.cc
|
||||||
src/base/crypto/Algorithm.cpp
|
src/base/crypto/Algorithm.cpp
|
||||||
src/base/crypto/Coin.cpp
|
src/base/crypto/Coin.cpp
|
||||||
src/base/crypto/keccak.cpp
|
src/base/crypto/keccak.cpp
|
||||||
|
@ -130,14 +132,14 @@ set(SOURCES_BASE
|
||||||
src/base/net/tools/LineReader.cpp
|
src/base/net/tools/LineReader.cpp
|
||||||
src/base/net/tools/NetBuffer.cpp
|
src/base/net/tools/NetBuffer.cpp
|
||||||
src/base/tools/Arguments.cpp
|
src/base/tools/Arguments.cpp
|
||||||
|
src/base/tools/cryptonote/BlockTemplate.cpp
|
||||||
|
src/base/tools/cryptonote/crypto-ops-data.c
|
||||||
|
src/base/tools/cryptonote/crypto-ops.c
|
||||||
|
src/base/tools/cryptonote/Signatures.cpp
|
||||||
|
src/base/tools/cryptonote/WalletAddress.cpp
|
||||||
src/base/tools/Cvt.cpp
|
src/base/tools/Cvt.cpp
|
||||||
src/base/tools/String.cpp
|
src/base/tools/String.cpp
|
||||||
src/base/tools/Timer.cpp
|
src/base/tools/Timer.cpp
|
||||||
src/base/tools/cryptonote/BlockTemplate.cpp
|
|
||||||
src/base/tools/cryptonote/Signatures.cpp
|
|
||||||
src/base/tools/cryptonote/WalletAddress.cpp
|
|
||||||
src/base/tools/cryptonote/crypto-ops.c
|
|
||||||
src/base/tools/cryptonote/crypto-ops-data.c
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -137,21 +137,21 @@ int64_t xmrig::DaemonClient::submit(const JobResult &result)
|
||||||
|
|
||||||
memcpy(data + m_job.nonceOffset() * 2, result.nonce, 8);
|
memcpy(data + m_job.nonceOffset() * 2, result.nonce, 8);
|
||||||
|
|
||||||
if (m_blocktemplate.has_miner_signature && result.sig) {
|
if (m_blocktemplate.hasMinerSignature() && result.sig) {
|
||||||
memcpy(data + sig_offset * 2, result.sig, 64 * 2);
|
memcpy(data + sig_offset * 2, result.sig, 64 * 2);
|
||||||
memcpy(data + m_blocktemplate.tx_pubkey_index * 2, result.sig_data, 32 * 2);
|
memcpy(data + m_blocktemplate.offset(BlockTemplate::TX_PUBKEY_OFFSET) * 2, result.sig_data, 32 * 2);
|
||||||
memcpy(data + m_blocktemplate.eph_public_key_index * 2, result.sig_data + 32 * 2, 32 * 2);
|
memcpy(data + m_blocktemplate.offset(BlockTemplate::EPH_PUBLIC_KEY_OFFSET) * 2, result.sig_data + 32 * 2, 32 * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.extra_nonce >= 0) {
|
if (result.extra_nonce >= 0) {
|
||||||
Cvt::toHex(data + m_blocktemplate.tx_extra_nonce_index * 2, 8, reinterpret_cast<const uint8_t*>(&result.extra_nonce), 4);
|
Cvt::toHex(data + m_blocktemplate.offset(BlockTemplate::TX_EXTRA_NONCE_OFFSET) * 2, 8, reinterpret_cast<const uint8_t*>(&result.extra_nonce), 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
# else
|
# else
|
||||||
|
|
||||||
Cvt::toHex(data + m_job.nonceOffset() * 2, 8, reinterpret_cast<const uint8_t*>(&result.nonce), 4);
|
Cvt::toHex(data + m_job.nonceOffset() * 2, 8, reinterpret_cast<const uint8_t*>(&result.nonce), 4);
|
||||||
|
|
||||||
if (m_blocktemplate.has_miner_signature) {
|
if (m_blocktemplate.hasMinerSignature()) {
|
||||||
Cvt::toHex(data + sig_offset * 2, 128, result.minerSignature(), 64);
|
Cvt::toHex(data + sig_offset * 2, 128, result.minerSignature(), 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,29 +376,29 @@ bool xmrig::DaemonClient::parseJob(const rapidjson::Value ¶ms, int *code)
|
||||||
String blocktemplate = Json::getString(params, kBlocktemplateBlob);
|
String blocktemplate = Json::getString(params, kBlocktemplateBlob);
|
||||||
|
|
||||||
if (blocktemplate.isNull()) {
|
if (blocktemplate.isNull()) {
|
||||||
return jobError("Empty block template received from daemon.");
|
return jobError("Empty block template received from daemon."); // FIXME
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_blocktemplate.Init(blocktemplate, m_coin)) {
|
if (!m_blocktemplate.parse(blocktemplate, m_coin)) {
|
||||||
return jobError("Invalid block template received from daemon.");
|
return jobError("Invalid block template received from daemon.");
|
||||||
}
|
}
|
||||||
|
|
||||||
# ifdef XMRIG_PROXY_PROJECT
|
# ifdef XMRIG_PROXY_PROJECT
|
||||||
const size_t k = m_blocktemplate.miner_tx_prefix_begin_index;
|
const size_t k = m_blocktemplate.offset(BlockTemplate::MINER_TX_PREFIX_OFFSET);
|
||||||
job.setMinerTx(
|
job.setMinerTx(
|
||||||
m_blocktemplate.raw_blob.data() + k,
|
m_blocktemplate.blob() + k,
|
||||||
m_blocktemplate.raw_blob.data() + m_blocktemplate.miner_tx_prefix_end_index,
|
m_blocktemplate.blob() + m_blocktemplate.offset(BlockTemplate::MINER_TX_PREFIX_END_OFFSET),
|
||||||
m_blocktemplate.eph_public_key_index - k,
|
m_blocktemplate.offset(BlockTemplate::EPH_PUBLIC_KEY_OFFSET) - k,
|
||||||
m_blocktemplate.tx_pubkey_index - k,
|
m_blocktemplate.offset(BlockTemplate::TX_PUBKEY_OFFSET) - k,
|
||||||
m_blocktemplate.tx_extra_nonce_index - k,
|
m_blocktemplate.offset(BlockTemplate::TX_EXTRA_NONCE_OFFSET) - k,
|
||||||
m_blocktemplate.tx_extra_nonce_size,
|
m_blocktemplate.txExtraNonce().size(),
|
||||||
m_blocktemplate.miner_tx_merkle_tree_branch
|
m_blocktemplate.minerTxMerkleTreeBranch()
|
||||||
);
|
);
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
m_blockhashingblob = Json::getString(params, "blockhashing_blob");
|
m_blockhashingblob = Json::getString(params, "blockhashing_blob");
|
||||||
|
|
||||||
if (m_blocktemplate.has_miner_signature) {
|
if (m_blocktemplate.hasMinerSignature()) {
|
||||||
if (m_pool.spendSecretKey().isEmpty()) {
|
if (m_pool.spendSecretKey().isEmpty()) {
|
||||||
return jobError("Secret spend key is not set.");
|
return jobError("Secret spend key is not set.");
|
||||||
}
|
}
|
||||||
|
@ -429,7 +429,7 @@ bool xmrig::DaemonClient::parseJob(const rapidjson::Value ¶ms, int *code)
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t derivation[32];
|
uint8_t derivation[32];
|
||||||
if (!generate_key_derivation(m_blocktemplate.raw_blob.data() + m_blocktemplate.tx_pubkey_index, secret_viewkey, derivation)) {
|
if (!generate_key_derivation(m_blocktemplate.blob(BlockTemplate::TX_PUBKEY_OFFSET), secret_viewkey, derivation)) {
|
||||||
return jobError("Failed to generate key derivation for miner signature.");
|
return jobError("Failed to generate key derivation for miner signature.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,7 +448,7 @@ bool xmrig::DaemonClient::parseJob(const rapidjson::Value ¶ms, int *code)
|
||||||
uint8_t eph_secret_key[32];
|
uint8_t eph_secret_key[32];
|
||||||
derive_secret_key(derivation, 0, secret_spendkey, eph_secret_key);
|
derive_secret_key(derivation, 0, secret_spendkey, eph_secret_key);
|
||||||
|
|
||||||
job.setEphemeralKeys(m_blocktemplate.raw_blob.data() + m_blocktemplate.eph_public_key_index, eph_secret_key);
|
job.setEphemeralKeys(m_blocktemplate.blob(BlockTemplate::EPH_PUBLIC_KEY_OFFSET), eph_secret_key);
|
||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,7 +458,7 @@ bool xmrig::DaemonClient::parseJob(const rapidjson::Value ¶ms, int *code)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_coin.isValid()) {
|
if (m_coin.isValid()) {
|
||||||
job.setAlgorithm(m_coin.algorithm(m_blocktemplate.major_version));
|
job.setAlgorithm(m_coin.algorithm(m_blocktemplate.majorVersion()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!job.setBlob(m_blockhashingblob)) {
|
if (!job.setBlob(m_blockhashingblob)) {
|
||||||
|
|
|
@ -352,16 +352,16 @@ void xmrig::Job::generateHashingBlob(String &blob) const
|
||||||
{
|
{
|
||||||
uint8_t root_hash[32];
|
uint8_t root_hash[32];
|
||||||
const uint8_t* p = m_minerTxPrefix.data();
|
const uint8_t* p = m_minerTxPrefix.data();
|
||||||
BlockTemplate::CalculateRootHash(p, p + m_minerTxPrefix.size(), m_minerTxMerkleTreeBranch, root_hash);
|
BlockTemplate::calculateRootHash(p, p + m_minerTxPrefix.size(), m_minerTxMerkleTreeBranch, root_hash);
|
||||||
|
|
||||||
uint64_t root_hash_offset = nonceOffset() + nonceSize();
|
uint64_t root_hash_offset = nonceOffset() + nonceSize();
|
||||||
|
|
||||||
if (m_hasMinerSignature) {
|
if (m_hasMinerSignature) {
|
||||||
root_hash_offset += BlockTemplate::SIGNATURE_SIZE + 2 /* vote */;
|
root_hash_offset += BlockTemplate::kSignatureSize + 2 /* vote */;
|
||||||
}
|
}
|
||||||
|
|
||||||
blob = rawBlob();
|
blob = rawBlob();
|
||||||
Cvt::toHex(blob.data() + root_hash_offset * 2, 64, root_hash, BlockTemplate::HASH_SIZE);
|
Cvt::toHex(blob.data() + root_hash_offset * 2, 64, root_hash, BlockTemplate::kHashSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -374,7 +374,7 @@ void xmrig::Job::generateMinerSignature(const uint8_t* blob, size_t size, uint8_
|
||||||
memcpy(tmp, blob, size);
|
memcpy(tmp, blob, size);
|
||||||
|
|
||||||
// Fill signature with zeros
|
// Fill signature with zeros
|
||||||
memset(tmp + nonceOffset() + nonceSize(), 0, BlockTemplate::SIGNATURE_SIZE);
|
memset(tmp + nonceOffset() + nonceSize(), 0, BlockTemplate::kSignatureSize);
|
||||||
|
|
||||||
uint8_t prefix_hash[32];
|
uint8_t prefix_hash[32];
|
||||||
xmrig::keccak(tmp, static_cast<int>(size), prefix_hash, sizeof(prefix_hash));
|
xmrig::keccak(tmp, static_cast<int>(size), prefix_hash, sizeof(prefix_hash));
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include "base/tools/Cvt.h"
|
#include "base/tools/Cvt.h"
|
||||||
#include "3rdparty/rapidjson/document.h"
|
#include "3rdparty/rapidjson/document.h"
|
||||||
|
|
||||||
|
@ -245,6 +244,12 @@ rapidjson::Value xmrig::Cvt::toHex(const Buffer &data, rapidjson::Document &doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
rapidjson::Value xmrig::Cvt::toHex(const Span &data, rapidjson::Document &doc)
|
||||||
|
{
|
||||||
|
return toHex(data.data(), data.size(), doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
rapidjson::Value xmrig::Cvt::toHex(const std::string &data, rapidjson::Document &doc)
|
rapidjson::Value xmrig::Cvt::toHex(const std::string &data, rapidjson::Document &doc)
|
||||||
{
|
{
|
||||||
return toHex(reinterpret_cast<const uint8_t *>(data.data()), data.size(), doc);
|
return toHex(reinterpret_cast<const uint8_t *>(data.data()), data.size(), doc);
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
#include "3rdparty/rapidjson/fwd.h"
|
#include "3rdparty/rapidjson/fwd.h"
|
||||||
#include "base/tools/Buffer.h"
|
#include "base/tools/Buffer.h"
|
||||||
|
#include "base/tools/Span.h"
|
||||||
#include "base/tools/String.h"
|
#include "base/tools/String.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,9 +38,11 @@ public:
|
||||||
inline static bool fromHex(Buffer &buf, const String &hex) { return fromHex(buf, hex.data(), hex.size()); }
|
inline static bool fromHex(Buffer &buf, const String &hex) { return fromHex(buf, hex.data(), hex.size()); }
|
||||||
inline static Buffer fromHex(const std::string &hex) { return fromHex(hex.data(), hex.size()); }
|
inline static Buffer fromHex(const std::string &hex) { return fromHex(hex.data(), hex.size()); }
|
||||||
inline static Buffer fromHex(const String &hex) { return fromHex(hex.data(), hex.size()); }
|
inline static Buffer fromHex(const String &hex) { return fromHex(hex.data(), hex.size()); }
|
||||||
inline static String toHex(const Buffer &data) { return toHex(data.data(), data.size()); }
|
|
||||||
inline static String toHex(const std::string &data) { return toHex(reinterpret_cast<const uint8_t *>(data.data()), data.size()); }
|
inline static String toHex(const std::string &data) { return toHex(reinterpret_cast<const uint8_t *>(data.data()), data.size()); }
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline static String toHex(const T &data) { return toHex(data.data(), data.size()); }
|
||||||
|
|
||||||
static bool fromHex(Buffer &buf, const char *in, size_t size);
|
static bool fromHex(Buffer &buf, const char *in, size_t size);
|
||||||
static bool fromHex(Buffer &buf, const rapidjson::Value &value);
|
static bool fromHex(Buffer &buf, const rapidjson::Value &value);
|
||||||
static bool fromHex(std::string &buf, const char *in, size_t size);
|
static bool fromHex(std::string &buf, const char *in, size_t size);
|
||||||
|
@ -49,6 +52,7 @@ public:
|
||||||
static Buffer fromHex(const char *in, size_t size);
|
static Buffer fromHex(const char *in, size_t size);
|
||||||
static Buffer randomBytes(size_t size);
|
static Buffer randomBytes(size_t size);
|
||||||
static rapidjson::Value toHex(const Buffer &data, rapidjson::Document &doc);
|
static rapidjson::Value toHex(const Buffer &data, rapidjson::Document &doc);
|
||||||
|
static rapidjson::Value toHex(const Span &data, rapidjson::Document &doc);
|
||||||
static rapidjson::Value toHex(const std::string &data, rapidjson::Document &doc);
|
static rapidjson::Value toHex(const std::string &data, rapidjson::Document &doc);
|
||||||
static rapidjson::Value toHex(const uint8_t *in, size_t size, rapidjson::Document &doc);
|
static rapidjson::Value toHex(const uint8_t *in, size_t size, rapidjson::Document &doc);
|
||||||
static String toHex(const uint8_t *in, size_t size);
|
static String toHex(const uint8_t *in, size_t size);
|
||||||
|
|
35
src/base/tools/Span.h
Normal file
35
src/base/tools/Span.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
/* XMRig
|
||||||
|
* Copyright (c) 2018-2021 SChernykh <https://github.com/SChernykh>
|
||||||
|
* Copyright (c) 2016-2021 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_SPAN_H
|
||||||
|
#define XMRIG_SPAN_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "3rdparty/epee/span.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace xmrig {
|
||||||
|
|
||||||
|
|
||||||
|
using Span = epee::span<const uint8_t>;
|
||||||
|
|
||||||
|
|
||||||
|
} /* namespace xmrig */
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* XMRIG_SPAN_H */
|
|
@ -24,11 +24,13 @@
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
|
||||||
namespace xmrig {
|
namespace xmrig {
|
||||||
|
|
||||||
|
|
||||||
|
template<bool EXCEPTIONS>
|
||||||
class BlobReader
|
class BlobReader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -45,7 +47,7 @@ public:
|
||||||
inline bool skip(size_t n)
|
inline bool skip(size_t n)
|
||||||
{
|
{
|
||||||
if (m_index + n > m_size) {
|
if (m_index + n > m_size) {
|
||||||
return false;
|
return outOfRange();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_index += n;
|
m_index += n;
|
||||||
|
@ -57,7 +59,7 @@ public:
|
||||||
inline bool operator()(uint8_t(&data)[N])
|
inline bool operator()(uint8_t(&data)[N])
|
||||||
{
|
{
|
||||||
if (m_index + N > m_size) {
|
if (m_index + N > m_size) {
|
||||||
return false;
|
return outOfRange();
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(data, m_data + m_index, N);
|
memcpy(data, m_data + m_index, N);
|
||||||
|
@ -67,19 +69,23 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline void readItems(T &data, size_t count)
|
inline bool operator()(T &data, size_t n)
|
||||||
{
|
{
|
||||||
data.resize(count);
|
if (m_index + n > m_size) {
|
||||||
for (size_t i = 0; i < count; ++i) {
|
return outOfRange();
|
||||||
operator()(data[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data = { m_data + m_index, n };
|
||||||
|
m_index += n;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline bool getByte(uint8_t &data)
|
inline bool getByte(uint8_t &data)
|
||||||
{
|
{
|
||||||
if (m_index >= m_size) {
|
if (m_index >= m_size) {
|
||||||
return false;
|
return outOfRange();
|
||||||
}
|
}
|
||||||
|
|
||||||
data = m_data[m_index++];
|
data = m_data[m_index++];
|
||||||
|
@ -95,8 +101,9 @@ private:
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (!getByte(t)) {
|
if (!getByte(t)) {
|
||||||
return false;
|
return outOfRange();
|
||||||
}
|
}
|
||||||
|
|
||||||
result |= static_cast<uint64_t>(t & 0x7F) << shift;
|
result |= static_cast<uint64_t>(t & 0x7F) << shift;
|
||||||
shift += 7;
|
shift += 7;
|
||||||
} while (t & 0x80);
|
} while (t & 0x80);
|
||||||
|
@ -106,6 +113,15 @@ private:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool outOfRange()
|
||||||
|
{
|
||||||
|
if (EXCEPTIONS) {
|
||||||
|
throw std::out_of_range("Blob read out of range");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const size_t m_size;
|
const size_t m_size;
|
||||||
const uint8_t *m_data;
|
const uint8_t *m_data;
|
||||||
size_t m_index = 0;
|
size_t m_index = 0;
|
||||||
|
|
|
@ -18,177 +18,64 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include "base/crypto/keccak.h"
|
|
||||||
#include "base/tools/Cvt.h"
|
|
||||||
#include "base/tools/cryptonote/BlobReader.h"
|
|
||||||
#include "base/tools/cryptonote/BlockTemplate.h"
|
#include "base/tools/cryptonote/BlockTemplate.h"
|
||||||
|
#include "3rdparty/rapidjson/document.h"
|
||||||
|
#include "base/crypto/keccak.h"
|
||||||
|
#include "base/tools/cryptonote/BlobReader.h"
|
||||||
|
#include "base/tools/Cvt.h"
|
||||||
|
|
||||||
|
|
||||||
namespace xmrig {
|
void xmrig::BlockTemplate::calculateMinerTxHash(const uint8_t *prefix_begin, const uint8_t *prefix_end, uint8_t *hash)
|
||||||
|
|
||||||
|
|
||||||
bool BlockTemplate::Init(const String& blockTemplate, Coin coin)
|
|
||||||
{
|
{
|
||||||
raw_blob = Cvt::fromHex(blockTemplate);
|
uint8_t hashes[kHashSize * 3];
|
||||||
|
|
||||||
BlobReader ar(raw_blob.data(), raw_blob.size());
|
|
||||||
|
|
||||||
// Block header
|
|
||||||
ar(major_version);
|
|
||||||
ar(minor_version);
|
|
||||||
ar(timestamp);
|
|
||||||
ar(prev_id);
|
|
||||||
ar(nonce);
|
|
||||||
|
|
||||||
// Wownero block template has miner signature starting from version 18
|
|
||||||
has_miner_signature = (coin == Coin::WOWNERO) && (major_version >= 18);
|
|
||||||
if (has_miner_signature) {
|
|
||||||
ar(miner_signature);
|
|
||||||
ar(vote);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
const uint64_t tx_extra_index = ar.index();
|
|
||||||
|
|
||||||
ar.readItems(extra, extra_size);
|
|
||||||
|
|
||||||
BlobReader ar_extra(extra.data(), extra_size);
|
|
||||||
|
|
||||||
tx_extra_nonce_size = 0;
|
|
||||||
tx_extra_nonce_index = 0;
|
|
||||||
|
|
||||||
while (ar_extra.index() < extra_size) {
|
|
||||||
uint64_t extra_tag = 0;
|
|
||||||
ar_extra(extra_tag);
|
|
||||||
|
|
||||||
switch (extra_tag) {
|
|
||||||
case 0x01: // TX_EXTRA_TAG_PUBKEY
|
|
||||||
tx_pubkey_index = tx_extra_index + ar_extra.index();
|
|
||||||
ar_extra.skip(KEY_SIZE);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x02: // TX_EXTRA_NONCE
|
|
||||||
ar_extra(tx_extra_nonce_size);
|
|
||||||
tx_extra_nonce_index = tx_extra_index + ar_extra.index();
|
|
||||||
ar_extra.skip(tx_extra_nonce_size);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false; // TODO: handle other tags
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
# ifdef XMRIG_PROXY_PROJECT
|
|
||||||
hashes.resize((num_hashes + 1) * HASH_SIZE);
|
|
||||||
CalculateMinerTxHash(raw_blob.data() + miner_tx_prefix_begin_index, raw_blob.data() + miner_tx_prefix_end_index, 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();
|
|
||||||
# endif
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void BlockTemplate::CalculateMinerTxHash(const uint8_t* prefix_begin, const uint8_t* prefix_end, uint8_t* hash)
|
|
||||||
{
|
|
||||||
uint8_t hashes[HASH_SIZE * 3];
|
|
||||||
|
|
||||||
// Calculate 3 partial hashes
|
// Calculate 3 partial hashes
|
||||||
|
|
||||||
// 1. Prefix
|
// 1. Prefix
|
||||||
keccak(prefix_begin, static_cast<int>(prefix_end - prefix_begin), hashes, HASH_SIZE);
|
keccak(prefix_begin, static_cast<int>(prefix_end - prefix_begin), hashes, kHashSize);
|
||||||
|
|
||||||
// 2. Base RCT, single 0 byte in miner tx
|
// 2. Base RCT, single 0 byte in miner tx
|
||||||
static const uint8_t known_second_hash[HASH_SIZE] = {
|
static const uint8_t known_second_hash[kHashSize] = {
|
||||||
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
|
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);
|
memcpy(hashes + kHashSize, known_second_hash, kHashSize);
|
||||||
|
|
||||||
// 3. Prunable RCT, empty in miner tx
|
// 3. Prunable RCT, empty in miner tx
|
||||||
memset(hashes + HASH_SIZE * 2, 0, HASH_SIZE);
|
memset(hashes + kHashSize * 2, 0, kHashSize);
|
||||||
|
|
||||||
// Calculate miner transaction hash
|
// Calculate miner transaction hash
|
||||||
keccak(hashes, sizeof(hashes), hash, HASH_SIZE);
|
keccak(hashes, sizeof(hashes), hash, kHashSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void BlockTemplate::CalculateMerkleTreeHash()
|
void xmrig::BlockTemplate::calculateRootHash(const uint8_t *prefix_begin, const uint8_t *prefix_end, const Buffer &miner_tx_merkle_tree_branch, uint8_t *root_hash)
|
||||||
{
|
{
|
||||||
miner_tx_merkle_tree_branch.clear();
|
calculateMinerTxHash(prefix_begin, prefix_end, root_hash);
|
||||||
|
|
||||||
const uint64_t count = num_hashes + 1;
|
for (size_t i = 0; i < miner_tx_merkle_tree_branch.size(); i += kHashSize) {
|
||||||
uint8_t* h = hashes.data();
|
uint8_t h[kHashSize * 2];
|
||||||
|
|
||||||
|
memcpy(h, root_hash, kHashSize);
|
||||||
|
memcpy(h + kHashSize, miner_tx_merkle_tree_branch.data() + i, kHashSize);
|
||||||
|
|
||||||
|
keccak(h, kHashSize * 2, root_hash, kHashSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void xmrig::BlockTemplate::calculateMerkleTreeHash()
|
||||||
|
{
|
||||||
|
m_minerTxMerkleTreeBranch.clear();
|
||||||
|
|
||||||
|
const uint64_t count = m_numHashes + 1;
|
||||||
|
const uint8_t *h = m_hashes.data();
|
||||||
|
|
||||||
if (count == 1) {
|
if (count == 1) {
|
||||||
memcpy(root_hash, h, HASH_SIZE);
|
memcpy(m_rootHash, h, kHashSize);
|
||||||
}
|
}
|
||||||
else if (count == 2) {
|
else if (count == 2) {
|
||||||
miner_tx_merkle_tree_branch.insert(miner_tx_merkle_tree_branch.end(), h + HASH_SIZE, h + HASH_SIZE * 2);
|
m_minerTxMerkleTreeBranch.insert(m_minerTxMerkleTreeBranch.end(), h + kHashSize, h + kHashSize * 2);
|
||||||
keccak(h, HASH_SIZE * 2, root_hash, HASH_SIZE);
|
keccak(h, kHashSize * 2, m_rootHash, kHashSize);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
size_t i, j, cnt;
|
size_t i, j, cnt;
|
||||||
|
@ -197,64 +84,222 @@ void BlockTemplate::CalculateMerkleTreeHash()
|
||||||
|
|
||||||
cnt >>= 1;
|
cnt >>= 1;
|
||||||
|
|
||||||
miner_tx_merkle_tree_branch.reserve(HASH_SIZE * (i - 1));
|
m_minerTxMerkleTreeBranch.reserve(kHashSize * (i - 1));
|
||||||
|
|
||||||
Buffer ints(cnt * HASH_SIZE);
|
Buffer ints(cnt * kHashSize);
|
||||||
memcpy(ints.data(), h, (cnt * 2 - count) * HASH_SIZE);
|
memcpy(ints.data(), h, (cnt * 2 - count) * kHashSize);
|
||||||
|
|
||||||
for (i = cnt * 2 - count, j = cnt * 2 - count; j < cnt; i += 2, ++j) {
|
for (i = cnt * 2 - count, j = cnt * 2 - count; j < cnt; i += 2, ++j) {
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
miner_tx_merkle_tree_branch.insert(miner_tx_merkle_tree_branch.end(), h + HASH_SIZE, h + HASH_SIZE * 2);
|
m_minerTxMerkleTreeBranch.insert(m_minerTxMerkleTreeBranch.end(), h + kHashSize, h + kHashSize * 2);
|
||||||
}
|
}
|
||||||
keccak(h + i * HASH_SIZE, HASH_SIZE * 2, ints.data() + j * HASH_SIZE, HASH_SIZE);
|
keccak(h + i * kHashSize, kHashSize * 2, ints.data() + j * kHashSize, kHashSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (cnt > 2) {
|
while (cnt > 2) {
|
||||||
cnt >>= 1;
|
cnt >>= 1;
|
||||||
for (i = 0, j = 0; j < cnt; i += 2, ++j) {
|
for (i = 0, j = 0; j < cnt; i += 2, ++j) {
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
miner_tx_merkle_tree_branch.insert(miner_tx_merkle_tree_branch.end(), ints.data() + HASH_SIZE, ints.data() + HASH_SIZE * 2);
|
m_minerTxMerkleTreeBranch.insert(m_minerTxMerkleTreeBranch.end(), ints.data() + kHashSize, ints.data() + kHashSize * 2);
|
||||||
}
|
}
|
||||||
keccak(ints.data() + i * HASH_SIZE, HASH_SIZE * 2, ints.data() + j * HASH_SIZE, HASH_SIZE);
|
keccak(ints.data() + i * kHashSize, kHashSize * 2, ints.data() + j * kHashSize, kHashSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
miner_tx_merkle_tree_branch.insert(miner_tx_merkle_tree_branch.end(), ints.data() + HASH_SIZE, ints.data() + HASH_SIZE * 2);
|
m_minerTxMerkleTreeBranch.insert(m_minerTxMerkleTreeBranch.end(), ints.data() + kHashSize, ints.data() + kHashSize * 2);
|
||||||
keccak(ints.data(), HASH_SIZE * 2, root_hash, HASH_SIZE);
|
keccak(ints.data(), kHashSize * 2, m_rootHash, kHashSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void BlockTemplate::CalculateRootHash(const uint8_t* prefix_begin, const uint8_t* prefix_end, const Buffer& miner_tx_merkle_tree_branch, uint8_t* root_hash)
|
bool xmrig::BlockTemplate::parse(const Buffer &blocktemplate, const Coin &coin, bool hashes)
|
||||||
{
|
{
|
||||||
CalculateMinerTxHash(prefix_begin, prefix_end, root_hash);
|
if (blocktemplate.size() < kMinSize) {
|
||||||
|
return false;
|
||||||
for (size_t i = 0; i < miner_tx_merkle_tree_branch.size(); i += HASH_SIZE) {
|
|
||||||
uint8_t h[HASH_SIZE * 2];
|
|
||||||
|
|
||||||
memcpy(h, root_hash, HASH_SIZE);
|
|
||||||
memcpy(h + HASH_SIZE, miner_tx_merkle_tree_branch.data() + i, HASH_SIZE);
|
|
||||||
|
|
||||||
keccak(h, HASH_SIZE * 2, root_hash, HASH_SIZE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_blob = blocktemplate;
|
||||||
|
m_coin = coin;
|
||||||
|
bool rc = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
rc = parse(hashes);
|
||||||
|
} catch (...) {}
|
||||||
|
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void BlockTemplate::GenerateHashingBlob()
|
bool xmrig::BlockTemplate::parse(const char *blocktemplate, size_t size, const Coin &coin, bool hashes)
|
||||||
{
|
{
|
||||||
hashingBlob.clear();
|
if (size < (kMinSize * 2) || !Cvt::fromHex(m_blob, blocktemplate, size)) {
|
||||||
hashingBlob.reserve(miner_tx_prefix_begin_index + HASH_SIZE + 3);
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
hashingBlob.assign(raw_blob.begin(), raw_blob.begin() + miner_tx_prefix_begin_index);
|
m_coin = coin;
|
||||||
hashingBlob.insert(hashingBlob.end(), root_hash, root_hash + HASH_SIZE);
|
bool rc = false;
|
||||||
|
|
||||||
uint64_t k = num_hashes + 1;
|
try {
|
||||||
|
rc = parse(hashes);
|
||||||
|
} catch (...) {}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool xmrig::BlockTemplate::parse(const rapidjson::Value &blocktemplate, const Coin &coin, bool hashes)
|
||||||
|
{
|
||||||
|
return blocktemplate.IsString() && parse(blocktemplate.GetString(), blocktemplate.GetStringLength(), coin, hashes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool xmrig::BlockTemplate::parse(const String &blocktemplate, const Coin &coin, bool hashes)
|
||||||
|
{
|
||||||
|
return parse(blocktemplate.data(), blocktemplate.size(), coin, hashes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void xmrig::BlockTemplate::generateHashingBlob(Buffer &out) const
|
||||||
|
{
|
||||||
|
out.clear();
|
||||||
|
out.reserve(offset(MINER_TX_PREFIX_OFFSET) + kHashSize + 3);
|
||||||
|
|
||||||
|
out.assign(m_blob.begin(), m_blob.begin() + offset(MINER_TX_PREFIX_OFFSET));
|
||||||
|
out.insert(out.end(), m_rootHash, m_rootHash + kHashSize);
|
||||||
|
|
||||||
|
uint64_t k = m_numHashes + 1;
|
||||||
while (k >= 0x80) {
|
while (k >= 0x80) {
|
||||||
hashingBlob.emplace_back((static_cast<uint8_t>(k) & 0x7F) | 0x80);
|
out.emplace_back((static_cast<uint8_t>(k) & 0x7F) | 0x80);
|
||||||
k >>= 7;
|
k >>= 7;
|
||||||
}
|
}
|
||||||
hashingBlob.emplace_back(static_cast<uint8_t>(k));
|
out.emplace_back(static_cast<uint8_t>(k));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} /* namespace xmrig */
|
bool xmrig::BlockTemplate::parse(bool hashes)
|
||||||
|
{
|
||||||
|
BlobReader<true> ar(m_blob.data(), m_blob.size());
|
||||||
|
|
||||||
|
// Block header
|
||||||
|
ar(m_version.first);
|
||||||
|
ar(m_version.second);
|
||||||
|
ar(m_timestamp);
|
||||||
|
ar(m_prevId, kHashSize);
|
||||||
|
|
||||||
|
setOffset(NONCE_OFFSET, ar.index());
|
||||||
|
ar.skip(kNonceSize);
|
||||||
|
|
||||||
|
// Wownero block template has miner signature starting from version 18
|
||||||
|
if (m_coin == Coin::WOWNERO && majorVersion() >= 18) {
|
||||||
|
ar(m_minerSignature, kSignatureSize);
|
||||||
|
ar(m_vote);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Miner transaction begin
|
||||||
|
// Prefix begin
|
||||||
|
setOffset(MINER_TX_PREFIX_OFFSET, ar.index());
|
||||||
|
|
||||||
|
ar(m_txVersion);
|
||||||
|
ar(m_unlockTime);
|
||||||
|
ar(m_numInputs);
|
||||||
|
|
||||||
|
// must be 1 input
|
||||||
|
if (m_numInputs != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ar(m_inputType);
|
||||||
|
|
||||||
|
// input type must be txin_gen (0xFF)
|
||||||
|
if (m_inputType != 0xFF) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ar(m_height);
|
||||||
|
ar(m_numOutputs);
|
||||||
|
|
||||||
|
// must be 1 output
|
||||||
|
if (m_numOutputs != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ar(m_amount);
|
||||||
|
ar(m_outputType);
|
||||||
|
|
||||||
|
// output type must be txout_to_key (2)
|
||||||
|
if (m_outputType != 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
setOffset(EPH_PUBLIC_KEY_OFFSET, ar.index());
|
||||||
|
|
||||||
|
ar(m_ephPublicKey, kKeySize);
|
||||||
|
ar(m_extraSize);
|
||||||
|
|
||||||
|
setOffset(TX_EXTRA_OFFSET, ar.index());
|
||||||
|
|
||||||
|
BlobReader<true> ar_extra(blob(TX_EXTRA_OFFSET), m_extraSize);
|
||||||
|
ar.skip(m_extraSize);
|
||||||
|
|
||||||
|
while (ar_extra.index() < m_extraSize) {
|
||||||
|
uint64_t extra_tag = 0;
|
||||||
|
ar_extra(extra_tag);
|
||||||
|
|
||||||
|
switch (extra_tag) {
|
||||||
|
case 0x01: // TX_EXTRA_TAG_PUBKEY
|
||||||
|
setOffset(TX_PUBKEY_OFFSET, offset(TX_EXTRA_OFFSET) + ar_extra.index());
|
||||||
|
ar_extra.skip(kKeySize);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x02: // TX_EXTRA_NONCE
|
||||||
|
{
|
||||||
|
uint64_t size = 0;
|
||||||
|
ar_extra(size);
|
||||||
|
setOffset(TX_EXTRA_NONCE_OFFSET, offset(TX_EXTRA_OFFSET) + ar_extra.index());
|
||||||
|
ar_extra(m_txExtraNonce, size);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false; // TODO: handle other tags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setOffset(MINER_TX_PREFIX_END_OFFSET, ar.index());
|
||||||
|
// Prefix end
|
||||||
|
|
||||||
|
// RCT signatures (empty in miner transaction)
|
||||||
|
uint8_t vin_rct_type = 0;
|
||||||
|
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 != offset(MINER_TX_PREFIX_END_OFFSET) + 1) || (*blob(MINER_TX_PREFIX_END_OFFSET) != 0)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other transaction hashes
|
||||||
|
ar(m_numHashes);
|
||||||
|
|
||||||
|
if (hashes) {
|
||||||
|
m_hashes.resize((m_numHashes + 1) * kHashSize);
|
||||||
|
calculateMinerTxHash(blob(MINER_TX_PREFIX_OFFSET), blob(MINER_TX_PREFIX_END_OFFSET), m_hashes.data());
|
||||||
|
|
||||||
|
for (uint64_t i = 1; i <= m_numHashes; ++i) {
|
||||||
|
Span h;
|
||||||
|
ar(h, kHashSize);
|
||||||
|
memcpy(m_hashes.data() + i * kHashSize, h.data(), kHashSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateMerkleTreeHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -22,71 +22,129 @@
|
||||||
#define XMRIG_BLOCKTEMPLATE_H
|
#define XMRIG_BLOCKTEMPLATE_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "3rdparty/rapidjson/fwd.h"
|
||||||
#include "base/crypto/Coin.h"
|
#include "base/crypto/Coin.h"
|
||||||
#include "base/tools/Buffer.h"
|
#include "base/tools/Buffer.h"
|
||||||
#include "base/tools/String.h"
|
#include "base/tools/String.h"
|
||||||
|
#include "base/tools/Span.h"
|
||||||
|
|
||||||
|
|
||||||
namespace xmrig {
|
namespace xmrig {
|
||||||
|
|
||||||
|
|
||||||
struct BlockTemplate
|
class BlockTemplate
|
||||||
{
|
{
|
||||||
enum {
|
public:
|
||||||
HASH_SIZE = 32,
|
static constexpr size_t kHashSize = 32;
|
||||||
KEY_SIZE = 32,
|
static constexpr size_t kKeySize = 32;
|
||||||
SIGNATURE_SIZE = 64,
|
static constexpr size_t kNonceSize = 4;
|
||||||
NONCE_SIZE = 4,
|
static constexpr size_t kSignatureSize = 64;
|
||||||
|
|
||||||
|
# ifdef XMRIG_PROXY_PROJECT
|
||||||
|
static constexpr bool kCalcHashes = true;
|
||||||
|
# else
|
||||||
|
static constexpr bool kCalcHashes = false;
|
||||||
|
# endif
|
||||||
|
|
||||||
|
enum Offset : uint32_t {
|
||||||
|
NONCE_OFFSET,
|
||||||
|
MINER_TX_PREFIX_OFFSET,
|
||||||
|
MINER_TX_PREFIX_END_OFFSET,
|
||||||
|
EPH_PUBLIC_KEY_OFFSET,
|
||||||
|
TX_EXTRA_OFFSET,
|
||||||
|
TX_PUBKEY_OFFSET,
|
||||||
|
TX_EXTRA_NONCE_OFFSET,
|
||||||
|
OFFSET_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
Buffer raw_blob;
|
inline const Coin &coin() const { return m_coin; }
|
||||||
size_t eph_public_key_index;
|
inline const uint8_t *blob() const { return m_blob.data(); }
|
||||||
size_t tx_pubkey_index;
|
inline const uint8_t *blob(Offset offset) const { return m_blob.data() + m_offsets[offset]; }
|
||||||
uint64_t tx_extra_nonce_size;
|
inline size_t offset(Offset offset) const { return m_offsets[offset]; }
|
||||||
size_t tx_extra_nonce_index;
|
inline size_t size() const { return m_blob.size(); }
|
||||||
size_t miner_tx_prefix_begin_index;
|
|
||||||
size_t miner_tx_prefix_end_index;
|
|
||||||
|
|
||||||
// Block header
|
// Block header
|
||||||
uint8_t major_version;
|
inline uint8_t majorVersion() const { return m_version.first; }
|
||||||
uint8_t minor_version;
|
inline uint8_t minorVersion() const { return m_version.second; }
|
||||||
uint64_t timestamp;
|
inline uint64_t timestamp() const { return m_timestamp; }
|
||||||
uint8_t prev_id[HASH_SIZE];
|
inline const Span &prevId() const { return m_prevId; }
|
||||||
uint8_t nonce[NONCE_SIZE];
|
inline const uint8_t *nonce() const { return blob(NONCE_OFFSET); }
|
||||||
|
|
||||||
bool has_miner_signature;
|
// Wownero miner signature
|
||||||
uint8_t miner_signature[SIGNATURE_SIZE];
|
inline bool hasMinerSignature() const { return !m_minerSignature.empty(); }
|
||||||
uint8_t vote[2];
|
inline const Span &minerSignature() const { return m_minerSignature; }
|
||||||
|
inline const uint8_t *vote() const { return m_vote; }
|
||||||
|
|
||||||
// Miner tx
|
// Miner tx
|
||||||
uint64_t tx_version;
|
inline uint64_t txVersion() const { return m_txVersion; }
|
||||||
uint64_t unlock_time;
|
inline uint64_t unlockTime() const { return m_unlockTime; }
|
||||||
uint64_t num_inputs;
|
inline uint64_t numInputs() const { return m_numInputs; }
|
||||||
uint8_t input_type;
|
inline uint8_t inputType() const { return m_inputType; }
|
||||||
uint64_t height;
|
inline uint64_t height() const { return m_height; }
|
||||||
uint64_t num_outputs;
|
inline uint64_t numOutputs() const { return m_numOutputs; }
|
||||||
uint64_t amount;
|
inline uint64_t amount() const { return m_amount; }
|
||||||
uint8_t output_type;
|
inline uint64_t outputType() const { return m_outputType; }
|
||||||
uint8_t eph_public_key[KEY_SIZE];
|
inline const Span &ephPublicKey() const { return m_ephPublicKey; }
|
||||||
uint64_t extra_size;
|
inline const Span &txExtraNonce() const { return m_txExtraNonce; }
|
||||||
Buffer extra;
|
|
||||||
uint8_t vin_rct_type;
|
|
||||||
|
|
||||||
// Transaction hashes
|
// Transaction hashes
|
||||||
uint64_t num_hashes;
|
inline uint64_t numHashes() const { return m_numHashes; }
|
||||||
Buffer hashes;
|
inline const Buffer &hashes() const { return m_hashes; }
|
||||||
|
inline const Buffer &minerTxMerkleTreeBranch() const { return m_minerTxMerkleTreeBranch; }
|
||||||
|
inline const uint8_t *rootHash() const { return m_rootHash; }
|
||||||
|
|
||||||
Buffer miner_tx_merkle_tree_branch;
|
inline Buffer generateHashingBlob() const
|
||||||
uint8_t root_hash[HASH_SIZE];
|
{
|
||||||
|
Buffer out;
|
||||||
|
generateHashingBlob(out);
|
||||||
|
|
||||||
Buffer hashingBlob;
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
bool Init(const String& blockTemplate, Coin coin);
|
static void calculateMinerTxHash(const uint8_t *prefix_begin, const uint8_t *prefix_end, uint8_t *hash);
|
||||||
|
static void calculateRootHash(const uint8_t *prefix_begin, const uint8_t *prefix_end, const Buffer &miner_tx_merkle_tree_branch, uint8_t *root_hash);
|
||||||
|
|
||||||
static void CalculateMinerTxHash(const uint8_t* prefix_begin, const uint8_t* prefix_end, uint8_t* hash);
|
bool parse(const Buffer &blocktemplate, const Coin &coin, bool hashes = kCalcHashes);
|
||||||
static void CalculateRootHash(const uint8_t* prefix_begin, const uint8_t* prefix_end, const Buffer& miner_tx_merkle_tree_branch, uint8_t* root_hash);
|
bool parse(const char *blocktemplate, size_t size, const Coin &coin, bool hashes);
|
||||||
void CalculateMerkleTreeHash();
|
bool parse(const rapidjson::Value &blocktemplate, const Coin &coin, bool hashes = kCalcHashes);
|
||||||
void GenerateHashingBlob();
|
bool parse(const String &blocktemplate, const Coin &coin, bool hashes = kCalcHashes);
|
||||||
|
void calculateMerkleTreeHash();
|
||||||
|
void generateHashingBlob(Buffer &out) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr size_t kMinSize = 76;
|
||||||
|
|
||||||
|
inline void setOffset(Offset offset, size_t value) { m_offsets[offset] = static_cast<uint32_t>(value); }
|
||||||
|
|
||||||
|
bool parse(bool hashes);
|
||||||
|
|
||||||
|
Buffer m_blob;
|
||||||
|
Coin m_coin;
|
||||||
|
uint32_t m_offsets[OFFSET_COUNT]{};
|
||||||
|
|
||||||
|
std::pair<uint8_t, uint8_t> m_version;
|
||||||
|
uint64_t m_timestamp = 0;
|
||||||
|
Span m_prevId;
|
||||||
|
|
||||||
|
Span m_minerSignature;
|
||||||
|
uint8_t m_vote[2]{};
|
||||||
|
|
||||||
|
uint64_t m_txVersion = 0;
|
||||||
|
uint64_t m_unlockTime = 0;
|
||||||
|
uint64_t m_numInputs = 0;
|
||||||
|
uint8_t m_inputType = 0;
|
||||||
|
uint64_t m_height = 0;
|
||||||
|
uint64_t m_numOutputs = 0;
|
||||||
|
uint64_t m_amount = 0;
|
||||||
|
uint8_t m_outputType = 0;
|
||||||
|
Span m_ephPublicKey;
|
||||||
|
uint64_t m_extraSize = 0;
|
||||||
|
Span m_txExtraNonce;
|
||||||
|
|
||||||
|
uint64_t m_numHashes = 0;
|
||||||
|
Buffer m_hashes;
|
||||||
|
Buffer m_minerTxMerkleTreeBranch;
|
||||||
|
uint8_t m_rootHash[kHashSize]{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ bool xmrig::WalletAddress::decode(const char *address, size_t size)
|
||||||
|
|
||||||
assert(data.size() == data_size);
|
assert(data.size() == data_size);
|
||||||
|
|
||||||
BlobReader ar(data.data(), data_size);
|
BlobReader<false> ar(data.data(), data_size);
|
||||||
|
|
||||||
if (ar(m_tag) && ar(m_publicSpendKey) && ar(m_publicViewKey) && ar.skip(ar.remaining() - sizeof(m_checksum)) && ar(m_checksum)) {
|
if (ar(m_tag) && ar(m_publicSpendKey) && ar(m_publicViewKey) && ar.skip(ar.remaining() - sizeof(m_checksum)) && ar(m_checksum)) {
|
||||||
uint8_t md[200];
|
uint8_t md[200];
|
||||||
|
|
Loading…
Reference in a new issue