Compare commits

...

26 Commits

Author SHA1 Message Date
Duke 54a38abafe Compile lxrhash.cpp directly to an object file instead of linking to it 3 months ago
Duke 3166f1359d fix ramhash pow 3 months ago
Duke ee6341e277 build ramhash 3 months ago
Duke 23af9a4bf8 Link to ramhash 3 months ago
Duke 5c17dbd207 add ramhash makefile 3 months ago
Duke 6663118e2a Use std::remove instead of std::filesystem::remove which is only available in C++17 3 months ago
Duke 1a49819e44 Compile fixes 3 months ago
Duke b853cb92a1 Compile fixes 3 months ago
Duke 470eacc6bd Merge branch 'duke' into ramhash 3 months ago
Duke 0864cb0550 Merge branch 'dev' into ramhash 4 months ago
Duke 04b122a067 Merge branch 'duke' into dev 4 months ago
Duke b70370123e Fix boost download link 4 months ago
Duke 323d2134a1 Boost download has been broken for over a week, so we will host our own 4 months ago
Duke 3f6c9ec651 Merge branch 'dev' into ramhash 4 months ago
Duke 22e0cfcb17 Merge branch 'dev' into ramhash 4 months ago
Duke d386a0439e Remove unused variables 5 months ago
Duke 0ce00a29ed Use sha256 of current block as input hash to LxrPow() 5 months ago
Duke aaea1a418b Include ramhash header 5 months ago
Duke dd45fed774 Add Ramhash C++ implementation 5 months ago
Duke e05e9b6fce Add most of the implementation of RamhashMiner() 5 months ago
Duke 61e801539d Add rhdebug() for ramhash-specific debug output 5 months ago
Duke 8f3e62bd65 Merge branch 'dev' into ramhash 5 months ago
Duke a9cbc8fa09 Merge branch 'dev' into ramhash 7 months ago
Duke f335fb539f We are calling it ramhash not memhash 9 months ago
Duke b40a6f6d34 Start implementing MemhashMiner 9 months ago
Duke f89cd61fce Add a new memhash option to ASSETCHAINS_ALGORITHMS 9 months ago
  1. 2
      src/Makefile.am
  2. 32
      src/Ramhash/.gitignore
  3. 99
      src/Ramhash/Makefile
  4. 24
      src/Ramhash/include/lxrhash.h
  5. 233
      src/Ramhash/src/lxrhash.cpp
  6. 68
      src/Ramhash/src/main.cpp
  7. 2
      src/hush_defs.h
  8. 6
      src/hush_globals.h
  9. 6
      src/init.cpp
  10. 305
      src/miner.cpp
  11. 2
      src/pow.cpp
  12. 1
      src/util.cpp
  13. 1
      src/util.h
  14. 5
      util/build.sh

2
src/Makefile.am

@ -62,6 +62,7 @@ LIBUNIVALUE=univalue/libunivalue.la
LIBZCASH=libzcash.a
LIBHUSH=libhush.a
LIBRANDOMX=RandomX/build/librandomx.a
#LIBRAMHASH=Ramhash/obj/liblxrhash.a
if BUILD_BITCOIN_LIBS
LIBZCASH_CONSENSUS=libzcashconsensus.la
@ -421,6 +422,7 @@ libbitcoin_common_a_SOURCES = \
script/standard.cpp \
transaction_builder.cpp \
cc/CCtokenutils.cpp \
Ramhash/src/lxrhash.cpp \
cc/CCutilbits.cpp \
$(BITCOIN_CORE_H) \
$(LIBZCASH_H)

32
src/Ramhash/.gitignore

@ -0,0 +1,32 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app

99
src/Ramhash/Makefile

@ -0,0 +1,99 @@
# define the name of application
APPNAME := lxrhash
# define the cpp compiler to use
CXX := g++
# define the FLAGS
FLAGS := -Wall
# define bin directory
BIN := bin
# define include directory
INC := include
# define lib directory
LIB := lib
# define source directory
SRC := src
# define object directory
OBJ := obj
VPATH := $(SRC) $(INC) $(LIB)
# Generate extension depending on the operating system
ifeq ($(OS),Windows_NT)
TARGET := $(BIN)/$(APPNAME).exe
else
TARGET := $(BIN)/$(APPNAME)
endif
# define the color of the console text
ifeq ($(OS),Windows_NT)
RED := $(tput setaf "1")
GREEN := $(tput setaf "2")
YELLOW := $(tput setaf "3")
BLUE := $(tput setaf "4")
PURPLE := $(tput setaf "5")
CYAN := $(tput setaf "6")
WHITE := $(tput setaf "7")
RESET := $(tput sgr0)
else
RED := $(shell echo -e "\033[031m")
GREEN := $(shell echo -e "\033[032m")
YELLOW := $(shell echo -e "\033[033m")
BLUE := $(shell echo -e "\033[034m")
PURPLE := $(shell echo -e "\033[035m")
CYAN := $(shell echo -e "\033[036m")
WHITE := $(shell echo -e "\033[037m")
RESET := $(shell echo -e "\033[0m")
endif
OBJS := $(patsubst %.cpp, $(OBJ)/%.o, $(notdir $(wildcard $(SRC)/*.cpp) $(wildcard $(LIB)/*.cpp)))
# Generate object files
$(OBJ)/%.o: %.cpp
ifeq ($(OS),Windows_NT)
@IF NOT EXIST $(subst /,\,$(OBJ)) mkdir $(subst /,\,$(OBJ))
else
@mkdir -p $(OBJ)
endif
@echo $(CYAN)Compiling: $(RED)$(@F)$(RESET)
@$(CXX) -I $(INC) -c $< -o $@
# Generate executable
$(TARGET): $(OBJS)
ifeq ($(OS),Windows_NT)
@IF NOT EXIST $(subst /,\,$(BIN)) mkdir $(subst /,\,$(BIN))
else
@mkdir -p $(BIN)
endif
@echo $(CYAN)Creating executable: $(GREEN)$(@F)$(RESET)
@$(CXX) $(OBJS) -o $@ $(FLAGS)
.PHONY: run
run:
@$(TARGET)
.PHONY: clean
clean:
ifeq ($(OS),Windows_NT)
@if exist $(OBJ)\*.o @del /Q $(OBJ)\*.o
@if exist $(OBJ)\*.exe @del /Q $(BIN)\*.exe
else
@rm -f $(OBJ)/*.o
@rm -f $(BIN)/*
endif
.PHONY: delete
delete:
ifeq ($(OS),Windows_NT)
@if exist $(OBJ) @rd /Q /S $(OBJ)
@if exist $(BIN) @rd /Q /S $(BIN)
else
@rm -rf $(OBJ)
@rm -rf $(BIN)
endif

24
src/Ramhash/include/lxrhash.h

@ -0,0 +1,24 @@
// Copyright 2023 The Accumulate Authors
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
unsigned long long const Size = 0x40000000; // Size of the BitMap. Should be 1 GB (0x40000000) for production
int const Passes = 6; //
int const Loops = 16; // How many times the hash is looped
unsigned long long const checksum = 0xed0536be7833ca15; // Checksum of byteMap to ensure we don't use a corrupted file
// Lookup XoR Hash (Ram Hash)
class LXRHash
{
unsigned char byteMap[Size]; // 1 GB is 30 bits, 2^30, or hex 4 with 7 zeros
unsigned long long MapMask = Size - 1; // MapMask is byteMap length -1
public: //
unsigned long long mix(char unsigned hash[32], unsigned long long nonce); // Mixes the hash with the nonce prior to
unsigned long long LxrPoW(char unsigned hash[32], unsigned long long nonce); // Returns the PoW for a hash + nonce
void init(); // Init the byteMap; do it only once.
private: //
bool load(); // Load the byteMap.dat file
unsigned long long check(); // Quick check of correct byteMap
};

233
src/Ramhash/src/lxrhash.cpp

@ -0,0 +1,233 @@
// Copyright 2023 The Accumulate Authors
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
#include <filesystem>
#include <iostream>
#include <fstream>
#include <cstdio>
#include <chrono>
#include <ctime>
#include "../include/lxrhash.h"
// LXRHash uses a combination of shifts and exclusive or's to combine inputs and states to
// create a hash with strong avalanche properties. This means that if any one bit is changed
// in the inputs, on average half of the bits of the output change. This quality makes an
// algorithmic attack on mining very, very unlikely.
//
// First, the hash and nonce are "mixed" such that a reasonable avalanche property is obtained.
//
// Secondly, the resulting 48 bytes are mapped through a 1 GB byteMap where random accesses have
// an even chance of producing a value between 0-255. Cycling through lookups from random bytes in
// the 1 GB space produces a resulting value to be graded.
//
// The Shift XoR Random Number Generator (RNG) was invented by George Marsaglia and published
// in 2003. Please refer to the readme on the LXRHashing repository.
// https://gitlab.com/accumulatenetwork/ecosystem/LXRMining/-/blob/main/README.md
//
// * No elements of the triplets are zero
// * All elements are relatively prime to each other
// * All elements Are small relative to an unsigned long long (64)
// * The right shift generally smaller than the left shifts
//
// In my testing, and according to Marsaglia, no elements of these triplets fitting this criteria
// perform better or worse. Adding constants and other outside elements help the distribution of
// values.
//
// Further, two different sponge states are used, mix() and LXRPoW()
//
// mix()
// Combines the 32 byte hash with the unsigned long long nonce. This is done as rapidly as possible
// with an avalanche property only enough to ensure repeated hashes with minimal bit changes to
// nonces do not gain an advantage during mining by tracing the same paths through the byteMap,
// making better use of the cache.
//
// LXRPoW()
// Accepts a 32 byte hash with the unsigned long long nonce. The sponge is initialized using mix().
// Then Each byte is translated using a sequenced Pseudo random sequenced index into the
// 1 GB byteMap. This process is repeated for a number of loops, building up a sponge value.
// More loops makes the hash slower and more dependent on the random access speed to the
// 1 GB byteMap. As processors grow in speed, and chips accommodate greater caches, the
// byteMap and the loop number increased.
//
// mix() is then used again to "squeeze out" the hash, where the high 8 bytes represent the
// possible PoW solution.
// check()
// Quick checksum, with a bit of bit spreading to make the checksum stronger
unsigned long long LXRHash::check()
{
unsigned long long state = 0;
for (int i = 0; i < Size; i += 8)
{
unsigned long long a = 0; // Pull a long Big Endian value from byteMap
for (int j = 0; j < 8; j++)
{
a <<= 8;
a ^= (unsigned long long)(byteMap[i + j]);
}
state ^= state << 23 ^ a << 11; // Randomize state, fold in the long from byteMap
state ^= state >> 3; // in the byteMap, running them through a
state ^= state << 17; // Marsaglia Xorshift RNG
}
return state; // Return resulting state as the checksum
}
// init()
// initializes the 1 GB byteMap
// Please note that I just wrote cheap code to cache the byteMap. Since it takes about 5 minutes
// or more to generate the byteMap, this code looks for a byteMap.dat file and loads it if it
// exists.
void LXRHash::init()
{
if (LXRHash::load())
{
return;
}
printf("\nbyteMap.dat file not found. Building byteMap.dat file is slow. This takes six passes:\n");
// Initialize the byteMap with values from 0 to 255 repeated for the whole array
for (unsigned long long i = 0; i < Size; i++)
{
byteMap[i] = i;
}
printf("start cs %llx\n", check());
unsigned long long offset = 0x7a43b26c611e135f; // Arbitrary 64 bit initial offset (no zero bytes)
unsigned long long cnt = 0; // Marsaglia found a counter improves Xorshift
for (unsigned long long i = 0; i < Passes; i++) // Passes. 2 passes at least are needed.
{ //
for (unsigned long long j = 0; j < Size; j++) // Shuffle the byteMap by swapping every byte with
{ // another randomly chosen byte.
offset ^= offset << 13 ^ cnt; // More classical Marsaglia Xorshift RNG
offset ^= offset >> 23; //
offset ^= offset << 5; //
unsigned long long r = offset & MapMask; // Mask r to an index within the byteMap
unsigned char a = byteMap[r]; // Get the random byte
byteMap[r] = byteMap[j]; // Put the ith value there
byteMap[j] = a; // Put the random byte at the ith position
cnt += a; // Walk cnt forward by a, (0-255)
if (j % 200000000 == 0) // Give feedback
{ //
printf("."); // A period every 200M bytes swapped
fflush(stdout); // flush so user can see
} //
} //
printf("%llu", i + 1); // Count the passes
fflush(stdout);
}
printf("\n");
printf("Checksum: %llx\n", check()); // Print the file checksum
std::fstream byteMapFile;
byteMapFile = std::fstream("byteMap.dat", std::ios::out | std::ios::binary);
byteMapFile.write((char *)(byteMap), Size);
byteMapFile.close();
}
bool LXRHash::load()
{
// open the file:
std::streampos fileSize;
std::ifstream file("byteMap.dat", std::ios::binary);
// get its size:
file.seekg(0, std::ios::end); // Seek to the end
fileSize = file.tellg(); // get the file size
if (fileSize != Size) // if not what we expect (exactly the length of byteMap)
{ //
return (false); // then return false (to generate the right file)
} //
file.seekg(0, std::ios::beg); // Seek to the start of the file,
file.read((char *)(byteMap), Size); // and read the full byteMap
file.close(); //
unsigned long long cs = check(); //
if (check() != checksum) // Then check that the checksum is correct
{ //
printf("checksum mismatch l:%llx != cs:%llx\n", cs, checksum);
std::remove("byteMap.dat"); // Remove the bad file
return (false); // Return fail
} //
return true; // If so, all good, don't need to regenerate the byteMap
}
unsigned long long LXRHash::LxrPoW(char unsigned hash[32], unsigned long long nonce)
{
// Mix the hash with the nonce to ensure the path through the byteMap is unique even if one bit
unsigned long long state = LXRHash::mix(hash, nonce); // is changed from another submission.
// Make the specified "loops" through the hash. The initial state is returned by Mix,
// bringing the total number of bits involved in the "sponge" to 48. Increasing the
// loops would slow down the hash, giving this mechanism an easy way to keep difficulty
// within the limits of the total hash power in a protocol
for (int i = 0; i < Loops; i++) // Make our loops over the
{ // entire hash
for (int j = 0; j < 32; j++) // Process each byte
{ //
unsigned long long b, v; // Do all math in unsigned long long
b = byteMap[state & MapMask]; // Pull a byte from the ByteMap
v = hash[j]; // Get a byte from the hash
state ^= state << 17 ^ b << 31; // A modified XorShift RNG
state ^= state >> 7 ^ v << 23; // Fold in a random byte from
state ^= state << 5; // the byteMap and the byte
hash[j] = state; // from the hash.
} // Then update the hash byte.
}
state = LXRHash::mix(hash, state); // Do a final mix() using state
return state; // as the nonce and return the state.
}
// mix()
// Mix spreads the bits of the hash and nonce throughout the hash and a "state"
unsigned long long LXRHash::mix(char unsigned hash[32], unsigned long long nonce)
{
unsigned long long state = 0xb32c25d16e362f32; // Set the state to an arbitrary unsigned long long with bits
nonce ^= nonce << 19 ^ state; // Run the nonce through a Xorshift
nonce ^= nonce >> 1;
nonce ^= nonce << 3;
unsigned long long array[5]; // Array of longs, the nonce, and 4 longs holding hash
array[0] = nonce; // this array along with the state is the sponge for the LXRHash
for (int i = 1; i < 5; i++) // Walk through the array
{ //
array[i] = 0; // Clear the array (not really needed, but it's cleaner)
for (int j = 0; j < 8; j++) // Get 8 bytes of the hash as array entry
{ //
array[i] <<= 8; // Shift the array to the left a byte
array[i] |= hash[((i - 1) << 3) + j]; // Or in the next byte
} //
state ^= state << 7 ^ array[i]; // shift state combine the whole hash into
state ^= state >> 3; // the state while randomizing the state
state ^= state << 5; // through a Xorshift RNG.
}
// mix what is effectively sponge of 5 elements and a state
for (int i = 0; i < 5; i++)
{
unsigned long long a, left1, right, left2;
a = array[i]; // Get each element of the array
left1 = (a & 0x1F) | 1; // Use the element to pick 3 random shifts for the Xorshift
right = ((a & 0xF0) >> 5) | 1; // Note that the right is smaller than the left
left2 = (((a & 0x1F0000) >> 17) << 1) ^ left1; // And xor-ing left1 with left2 ensures they are different
state ^= state << left1 ^ a << 5; // Randomize the state and array element
state ^= state >> right; // using the random shifts
state ^= state << left2; //
array[i] ^= state; // apply to the array
}
// Extract the 32 byte hash into the *hash passed in.
for (int i = 0; i < 4; i++)
{
unsigned long long a = array[i] ^ state;
for (int j = 0; j < 8; j++) // for 8 bytes
{ // Put each byte into hash, Big Endian order
hash[i * 8 + j] = a >> ((7 - j) * 8); // i indexes 8 byte group, j each byte
} //
state ^= state << 27 ^ i << 19; // Mix the state a bit more, fold in the counter
state ^= state >> 11; //
state ^= state << 13; //
}
return state;
}

68
src/Ramhash/src/main.cpp

@ -0,0 +1,68 @@
// Copyright 2023 The Accumulate Authors
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
#include <iostream>
#include <fstream>
#include <cstdio>
#include <chrono>
#include <ctime>
#include "../include/lxrhash.h"
LXRHash lxrHash;
int main()
{
printf("Load byteMap\n");
lxrHash.init(); // init() loads/computes byteMap
printf("The byteMap is loaded, mining begins!\n"); //
printf("%13s %13s %10s %16s %15s\n", //
"total(s)", "block(s)", "nonce", "PoW", "hashes/sec"); //
unsigned char hash[32]; // Start with a hash of all zeros
std::fill_n(hash, 32, 0); //
unsigned long long hashCnt = 0; // Count hashes to compute hash/second (hps)
unsigned long long ms = 0; // Set the current time passed to zero
for (int i = 0; i < 1000; i++) //
{ //
printf("Now Mining: "); // Start mining the new hash
for (int i = 0; i < 32; i++) // Print the new hash in hex
{ //
printf("%02x", hash[i]); //
} //
printf("\n"); //
auto last = std::chrono::system_clock::now(); // time
unsigned long long ms1 = 0; // block time
unsigned long long min = 0xFFFFFFFFFFFFFFFF; // set min to max unsigned long long value
bool done = false; // Done is set when a block difficulty is found
for (unsigned long long nonce = 0; !done; nonce++, hashCnt++) // Loop until done, count nonces and hashes
{ //
unsigned char tmpHash[32]; // Copy hash since LxrPoW will overwrite
std::copy(hash, hash + 32, tmpHash); //
unsigned long long v = lxrHash.LxrPoW(tmpHash, nonce); // Get the LxrPow for current nonce
if (v < min && v < (unsigned long long)(1) << 52) // Print new solutions < some minimum difficulty
{ //
min = v; // Set as the new highest solution found
auto end = std::chrono::system_clock::now(); // Calculate time stamps
unsigned long long ms2 = //
std::chrono::duration_cast<std::chrono::milliseconds>(end - last).count();
ms1 += ms2; // Add to the time in this block
ms += ms2; // Add to total time spent
last = std::chrono::system_clock::now(); // Reset timer for this block
printf("%13.3f %13.3f %10llu %016llx %15.3f \n", // Print out the new solution
float(ms) / 1000, // Total time
float(ms1) / 1000, // Time mining this block
nonce, // The nonce solution found
v, // The mining value using the nonce
float(hashCnt) / (float(ms) / 1000 + 1)); // Total hashes per second in this test
} //
done = v < (unsigned long long)(1) << 45; // Check against the difficulty for EOB
if (done) // If at EOB
{ // then set up a new block to mine
std::copy(tmpHash, tmpHash + 32, hash); // Use the tmpHash as a new hash!
}
}
}
return 0;
}

2
src/hush_defs.h

@ -572,7 +572,7 @@ extern int32_t ASSETCHAINS_LWMAPOS, ASSETCHAINS_SAPLING, ASSETCHAINS_OVERWINTER,
extern uint64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_FOUNDERS_REWARD;
extern int32_t ASSETCHAINS_LWMAPOS, ASSETCHAINS_SAPLING, ASSETCHAINS_OVERWINTER,ASSETCHAINS_BLOCKTIME;
extern uint64_t ASSETCHAINS_TIMELOCKGTE;
extern uint32_t ASSETCHAINS_ALGO,ASSETCHAINS_EQUIHASH,ASSETCHAINS_RANDOMX, HUSH_INITDONE;
extern uint32_t ASSETCHAINS_ALGO,ASSETCHAINS_EQUIHASH,ASSETCHAINS_RANDOMX,ASSETCHAINS_RAMHASH, HUSH_INITDONE;
extern int32_t HUSH_MININGTHREADS,HUSH_LONGESTCHAIN,ASSETCHAINS_SEED,IS_HUSH_NOTARY,USE_EXTERNAL_PUBKEY,HUSH_CHOSEN_ONE,HUSH_ON_DEMAND,HUSH_PASSPORT_INITDONE,ASSETCHAINS_STAKED,HUSH_NSPV;
extern uint64_t ASSETCHAINS_COMMISSION, ASSETCHAINS_LASTERA,ASSETCHAINS_CBOPRET;
extern uint64_t ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS+1], ASSETCHAINS_NOTARY_PAY[ASSETCHAINS_MAX_ERAS+1], ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_NONCEMASK[],ASSETCHAINS_NK[2];

6
src/hush_globals.h

@ -81,11 +81,13 @@ std::vector<std::string> ASSETCHAINS_PRICES,ASSETCHAINS_STOCKS;
// this is the offset in the ASSETCHAINS_ALGORITHMS array
#define _ASSETCHAINS_EQUIHASH 0
#define _ASSETCHAINS_RANDOMX 1
#define _ASSETCHAINS_RAMHASH 2
uint32_t ASSETCHAINS_NUMALGOS = 4; // there are different variants of equihash with different (N,K)
uint32_t ASSETCHAINS_NUMALGOS = 5; // there are different variants of equihash with different (N,K)
uint32_t ASSETCHAINS_EQUIHASH = _ASSETCHAINS_EQUIHASH;
uint32_t ASSETCHAINS_RANDOMX = _ASSETCHAINS_RANDOMX;
const char *ASSETCHAINS_ALGORITHMS[] = {"equihash", "randomx"};
uint32_t ASSETCHAINS_RAMHASH = _ASSETCHAINS_RAMHASH;
const char *ASSETCHAINS_ALGORITHMS[] = {"equihash", "randomx", "ramhash"};
uint64_t ASSETCHAINS_NONCEMASK[] = {0xffff};
uint32_t ASSETCHAINS_NONCESHIFT[] = {32};
uint32_t ASSETCHAINS_HASHESPERROUND[] = {1};

6
src/init.cpp

@ -1293,7 +1293,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
fprintf(stderr,"%s: enabled randomx debug\n", __func__);
}
//fprintf(stderr,"%s tik5\n", __FUNCTION__);
if (find(categories.begin(), categories.end(), string("ramhash")) != categories.end()) {
fRamhashDebug = true;
fprintf(stderr,"%s: enabled ramhash debug\n", __func__);
}
// Check for -debugnet
if (GetBoolArg("-debugnet", false))
InitWarning(_("Warning: Unsupported argument -debugnet ignored, use -debug=net."));

305
src/miner.cpp

@ -31,6 +31,7 @@
#ifdef ENABLE_MINING
#include "crypto/equihash.h"
#include "RandomX/src/randomx.h"
#include "Ramhash/include/lxrhash.h"
#endif
#include "hash.h"
#include "key_io.h"
@ -58,6 +59,7 @@
#include <mutex>
#define rxdebug(format, ...) if(fRandomXDebug) { fprintf(stderr, format, __func__, ## __VA_ARGS__ ); }
#define rhdebug(format, ...) if(fRamhashDebug) { fprintf(stderr, format, __func__, ## __VA_ARGS__ ); }
using namespace std;
@ -117,8 +119,8 @@ public:
};
extern int8_t ASSETCHAINS_ADAPTIVEPOW;
extern uint32_t ASSETCHAINS_RANDOMX;
extern bool fRandomXDebug;
extern uint32_t ASSETCHAINS_RANDOMX, ASSETCHAINS_RAMHASH;
extern bool fRandomXDebug, fRamhashDebug;
extern std::string devtax_scriptpub_for_height(uint32_t nHeight);
void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
@ -971,7 +973,6 @@ static bool ProcessBlockFound(CBlock* pblock)
}
int32_t hush_baseid(char *origbase);
int32_t hush_eligiblenotary(uint8_t pubkeys[66][33],int32_t *mids,uint32_t *blocktimes,int32_t *nonzpkeysp,int32_t height);
arith_uint256 hush_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t height,int32_t goalperc);
int32_t FOUND_BLOCK,HUSH_MAYBEMINED;
extern int32_t HUSH_LASTMINED,HUSH_INSYNC;
@ -1052,6 +1053,18 @@ class RandomXSolverCanceledException : public std::exception
}
};
class RamhashSolverCanceledException : public std::exception
{
virtual const char* what() const throw() {
return "Ramhash solver was canceled";
}
};
enum RamhashSolverCancelCheck
{
RamhashReason1
};
enum RandomXSolverCancelCheck
{
Reason1,
@ -1483,6 +1496,288 @@ void static RandomXMiner()
c.disconnect();
}
#ifdef ENABLE_WALLET
void static RamhashMiner(CWallet *pwallet)
#else
void static RamhashMiner()
#endif
{
LogPrintf("HushRamhashMiner started\n");
SetThreadPriority(THREAD_PRIORITY_LOWEST);
RenameThread("hush-ramhash");
const CChainParams& chainparams = Params();
#ifdef ENABLE_WALLET
// Each thread has its own key
CReserveKey reservekey(pwallet);
#endif
// Each thread has its own counter
unsigned int nExtraNonce = 0;
uint8_t *script; uint64_t total; int32_t i,j,gpucount=HUSH_MAXGPUCOUNT,notaryid = -1;
while ( (ASSETCHAIN_INIT == 0 || HUSH_INITDONE == 0) )
{
sleep(1);
if ( hush_baseid(SMART_CHAIN_SYMBOL) < 0 )
break;
}
std::mutex m_cs;
bool cancelSolver = false;
boost::signals2::connection c = uiInterface.NotifyBlockTip.connect(
[&m_cs, &cancelSolver](const uint256& hashNewTip) mutable {
std::lock_guard<std::mutex> lock{m_cs};
cancelSolver = true;
}
);
miningTimer.start();
LXRHash ramhash;
ramhash.init();
rhdebug("%s: bytemap loaded\n");
try {
fprintf(stderr,"RamhashMiner: mining %s with ramhash\n",SMART_CHAIN_SYMBOL);
while(true) {
fprintf(stderr,"RamhashMiner: beginning mining loop on %s with nExtraNonce=%u\n",SMART_CHAIN_SYMBOL, nExtraNonce);
if (chainparams.MiningRequiresPeers()) {
miningTimer.stop();
do {
bool fvNodesEmpty;
{
fvNodesEmpty = vNodes.empty();
}
if (!fvNodesEmpty )
break;
MilliSleep(15000);
} while (true);
miningTimer.start();
}
// Create new block
unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
CBlockIndex* pindexPrev = chainActive.LastTip();
// If we don't have a valid chain tip to work from, wait and try again.
if (pindexPrev == nullptr) {
fprintf(stderr,"%s: null pindexPrev, trying again...\n",__func__);
MilliSleep(1000);
continue;
}
if ( Mining_height != pindexPrev->GetHeight()+1 )
{
Mining_height = pindexPrev->GetHeight()+1;
Mining_start = (uint32_t)time(NULL);
}
#ifdef ENABLE_WALLET
CBlockTemplate *ptr = CreateNewBlockWithKey(reservekey, pindexPrev->GetHeight()+1, gpucount, 0);
#else
CBlockTemplate *ptr = CreateNewBlockWithKey();
#endif
if ( ptr == 0 )
{
if ( !GetBoolArg("-gen",false))
{
miningTimer.stop();
c.disconnect();
LogPrintf("HushRamhashMiner terminated\n");
return;
}
static uint32_t counter;
if ( counter++ < 10 )
fprintf(stderr,"RamhashMiner: created illegal blockB, retry with counter=%u\n", counter);
sleep(1);
continue;
}
unique_ptr<CBlockTemplate> pblocktemplate(ptr);
if (!pblocktemplate.get())
{
if (GetArg("-mineraddress", "").empty()) {
LogPrintf("Error in HushRamhashMiner: Keypool ran out, please call keypoolrefill before restarting the mining thread\n");
} else {
// Should never reach here, because -mineraddress validity is checked in init.cpp
LogPrintf("Error in HushRamhashMiner: Invalid -mineraddress\n");
}
return;
}
CBlock *pblock = &pblocktemplate->block;
if ( ASSETCHAINS_REWARD[0] == 0 && !ASSETCHAINS_LASTERA )
{
if ( pblock->vtx.size() == 1 && pblock->vtx[0].vout.size() == 1 && Mining_height > ASSETCHAINS_MINHEIGHT )
{
static uint32_t counter;
if ( counter++ < 10 )
fprintf(stderr,"skip generating %s on-demand block, no tx avail\n",SMART_CHAIN_SYMBOL);
sleep(10);
continue;
} else fprintf(stderr,"%s vouts.%d mining.%d vs %d\n",SMART_CHAIN_SYMBOL,(int32_t)pblock->vtx[0].vout.size(),Mining_height,ASSETCHAINS_MINHEIGHT);
}
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
LogPrintf("Running HushRamhashMiner with %u transactions in block (%u bytes)\n",pblock->vtx.size(),::GetSerializeSize(*pblock,SER_NETWORK,PROTOCOL_VERSION));
// Search
uint32_t savebits;
int64_t nStart = GetTime();
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus());
savebits = pblock->nBits;
HASHTarget = arith_uint256().SetCompact(savebits);
roundrobin_delay = ROUNDROBIN_DELAY;
Mining_start = 0;
gotinvalid = 0;
while (true)
{
if ( gotinvalid != 0 ) {
fprintf(stderr,"HushRamhashMiner: gotinvalid=%d\n",gotinvalid);
break;
}
hush_longestchain();
fprintf(stderr,"HushRamhashMiner: solving with nNonce = %s\n",pblock->nNonce.ToString().c_str());
arith_uint256 hashTarget;
hashTarget = HASHTarget;
CDataStream ramhashInput(SER_NETWORK, PROTOCOL_VERSION);
ramhashInput << pblocktemplate->block;
// Use the sha256 of current block as input hash to LxrPoW()
CSHA256 hasher;
hasher.Write((unsigned char*) ramhashInput.str().data(), ramhashInput.size());
unsigned char ramHash[CSHA256::OUTPUT_SIZE];
hasher.Finalize(ramHash);
// calculate actual ramhash hash with sha256 hash and nonce as input
//uint32_t rhnonce = UintToArith256(pblock->nNonce);
uint32_t rhnonce = UintToArith256( pblock->nNonce) .GetCompact();
// unsigned long long rhnonce = pblock->nNonce;
unsigned long long state = ramhash.LxrPoW(ramHash, (unsigned long long) rhnonce);
// Use hash to build a valid block
std::function<bool(std::vector<unsigned char>)> validBlock =
#ifdef ENABLE_WALLET
[&pblock, &hashTarget, &pwallet, &reservekey, &m_cs, &cancelSolver, &chainparams]
#else
[&pblock, &hashTarget, &m_cs, &cancelSolver, &chainparams]
#endif
(std::vector<unsigned char> soln) {
int32_t z; arith_uint256 h; CBlock B;
// Write the solution to the hash and compute the result.
rhdebug("%s: Checking solution against target\n");
pblock->nSolution = soln;
solutionTargetChecks.increment();
// fprintf(stderr,"%s: solutionTargetChecks=%lu\n", __func__, solutionTargetChecks.get());
B = *pblock;
h = UintToArith256(B.GetHash());
rhdebug("%s: h=");
if (fRamhashDebug) {
for (z=31; z>=0; z--)
fprintf(stderr,"%02x",((uint8_t *)&h)[z]);
fprintf(stderr," , hashTarget=");
for (z=31; z>=0; z--)
fprintf(stderr,"%02x",((uint8_t *)&hashTarget)[z]);
fprintf(stderr,"\n");
}
if ( h > hashTarget )
{
rhdebug("%s: h > hashTarget");
return false;
}
CValidationState state;
if ( !TestBlockValidity(state,B, chainActive.LastTip(), true, false))
{
h = UintToArith256(B.GetHash());
fprintf(stderr,"RamhashMiner: Invalid ramhash block mined, try again ");
for (z=31; z>=0; z--)
fprintf(stderr,"%02x",((uint8_t *)&h)[z]);
gotinvalid = 1;
fprintf(stderr,"\n");
return(false);
}
SetThreadPriority(THREAD_PRIORITY_NORMAL);
LogPrintf("HushRamhashMiner:\n");
LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", B.GetHash().GetHex(), HASHTarget.GetHex());
#ifdef ENABLE_WALLET
if (ProcessBlockFound(&B, *pwallet, reservekey)) {
#else
if (ProcessBlockFound(&B)) {
#endif
// Ignore chain updates caused by us
std::lock_guard<std::mutex> lock{m_cs};
cancelSolver = false;
}
SetThreadPriority(THREAD_PRIORITY_LOWEST);
// In regression test mode, stop mining after a block is found.
if (chainparams.MineBlocksOnDemand()) {
// Increment here because throwing skips the call below
// TODO: equivalent of ehSolverRuns.increment();
throw boost::thread_interrupted();
}
return true;
};
std::function<bool(RamhashSolverCancelCheck)> cancelled = [&m_cs, &cancelSolver](RamhashSolverCancelCheck pos) {
std::lock_guard<std::mutex> lock{m_cs};
return cancelSolver;
};
try {
// Use the ramhash hash as the block solution
std::vector<unsigned char> sol_char(ramHash, ramHash+32);
bool found = validBlock(sol_char);
if (found) {
rhdebug("%s: found solution!\n");
// If we find a POW solution, do not try other solutions
// because they become invalid as we created a new block in blockchain.
break;
} else {
rhdebug("%s: solution not found, validBlock=false\n");
}
} catch (RamhashSolverCanceledException&) {
LogPrintf("HushRamhashMiner solver canceled\n");
std::lock_guard<std::mutex> lock{m_cs};
cancelSolver = false;
}
boost::this_thread::interruption_point();
if (vNodes.empty() && chainparams.MiningRequiresPeers())
{
if ( Mining_height > ASSETCHAINS_MINHEIGHT )
{
fprintf(stderr,"%s: no nodes, break\n", __func__);
break;
}
}
if ((UintToArith256(pblock->nNonce) & 0xffff) == 0xffff)
{
fprintf(stderr,"%s: nonce & 0xffff == 0xffff, break\n", __func__);
break;
}
// Update nNonce and nTime
pblock->nNonce = ArithToUint256(UintToArith256(pblock->nNonce) + 1);
pblock->nBits = savebits;
}
} // while(true)
} catch (const boost::thread_interrupted&) {
miningTimer.stop();
c.disconnect();
LogPrintf("HushRamhashMiner terminated\n");
throw;
} catch (const std::runtime_error &e) {
miningTimer.stop();
c.disconnect();
fprintf(stderr,"RamhashMiner: runtime error: %s\n", e.what());
return;
}
miningTimer.stop();
c.disconnect();
}
#ifdef ENABLE_WALLET
void static BitcoinMiner(CWallet *pwallet)
#else
@ -1940,12 +2235,16 @@ void static BitcoinMiner()
minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet));
} else if (ASSETCHAINS_ALGO == ASSETCHAINS_RANDOMX ) {
minerThreads->create_thread(boost::bind(&RandomXMiner, pwallet));
} else if (ASSETCHAINS_ALGO == ASSETCHAINS_RAMHASH ) {
minerThreads->create_thread(boost::bind(&RamhashMiner, pwallet));
}
#else
if (ASSETCHAINS_ALGO == ASSETCHAINS_EQUIHASH ) {
minerThreads->create_thread(&BitcoinMiner);
} else if (ASSETCHAINS_ALGO == ASSETCHAINS_RANDOMX) {
minerThreads->create_thread(&RandomXMiner);
} else if (ASSETCHAINS_ALGO == ASSETCHAINS_RAMHASH ) {
minerThreads->create_thread(boost::bind(&RamhashMiner));
}
#endif
}

2
src/pow.cpp

@ -298,7 +298,7 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead
LogPrintf("%s: Using blocktime=%d\n",__func__,ASSETCHAINS_BLOCKTIME);
}
//if (ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH && ASSETCHAINS_STAKED == 0)
if (ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH && ASSETCHAINS_ALGO != ASSETCHAINS_RANDOMX) {
if (ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH && ASSETCHAINS_ALGO != ASSETCHAINS_RANDOMX && ASSETCHAINS_ALGO != ASSETCHAINS_RAMHASH) {
fprintf(stderr,"%s: using lwma for next work\n",__func__);
return lwmaGetNextWorkRequired(pindexLast, pblock, params);
}

1
src/util.cpp

@ -120,6 +120,7 @@ map<string, string> mapArgs;
map<string, vector<string> > mapMultiArgs;
bool fDebug = false;
bool fRandomXDebug = false;
bool fRamhashDebug = false;
bool fZdebug = false;
bool fPrintToConsole = false;
bool fPrintToDebugLog = true;

1
src/util.h

@ -59,6 +59,7 @@ extern std::map<std::string, std::string> mapArgs;
extern std::map<std::string, std::vector<std::string> > mapMultiArgs;
extern bool fDebug;
extern bool fRandomXDebug;
extern bool fRamhashDebug;
extern bool fZdebug;
extern bool fPrintToConsole;
extern bool fPrintToDebugLog;

5
util/build.sh

@ -140,4 +140,9 @@ fi
cd $WD
cd src/Ramhash
make
cd $WD
"$MAKE" "$@" V=1

Loading…
Cancel
Save