Hush Full Node software. We were censored from Github, this is where all development happens now. https://hush.is
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1956 lines
86 KiB

// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
3 months ago
// Copyright (c) 2016-2024 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
/******************************************************************************
* Copyright © 2014-2019 The SuperNET Developers. *
* *
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
* the top-level directory of this distribution for the individual copyright *
* holder information and the developer policies on copyright and licensing. *
* *
* Unless otherwise agreed in a custom licensing agreement, no part of the *
* SuperNET software, including this file may be copied, modified, propagated *
* or distributed except according to the terms contained in the LICENSE file *
* *
* Removal or modification of this copyright notice is prohibited. *
* *
******************************************************************************/
5 years ago
#include "pubkey.h"
#include "miner.h"
#ifdef ENABLE_MINING
#include "pow/tromp/equi_miner.h"
#endif
#include "amount.h"
#include "chainparams.h"
#include "consensus/consensus.h"
#include "consensus/upgrades.h"
#include "consensus/validation.h"
#ifdef ENABLE_MINING
#include "crypto/equihash.h"
#include "RandomX/src/randomx.h"
#endif
#include "hash.h"
#include "key_io.h"
#include "main.h"
#include "metrics.h"
#include "net.h"
#include "pow.h"
#include "primitives/transaction.h"
#include "random.h"
#include "timedata.h"
#include "ui_interface.h"
#include "util.h"
#include "utilmoneystr.h"
#ifdef ENABLE_WALLET
#include "wallet/wallet.h"
#endif
6 years ago
#include "zcash/Address.hpp"
#include "transaction_builder.h"
#include "sodium.h"
#include <boost/thread.hpp>
#include <boost/tuple/tuple.hpp>
#ifdef ENABLE_MINING
#include <functional>
#endif
#include <mutex>
#define rxdebug(format, ...) if(fRandomXDebug) { fprintf(stderr, format, __func__, ## __VA_ARGS__ ); }
using namespace std;
//////////////////////////////////////////////////////////////////////////////
//
// BitcoinMiner
//
//
// Unconfirmed transactions in the memory pool often depend on other
// transactions in the memory pool. When we select transactions from the
// pool, we select by highest priority or fee rate, so we might consider
// transactions that depend on transactions that aren't yet in the block.
// The COrphan class keeps track of these 'temporary orphans' while
// CreateBlock is figuring out which transactions to include.
//
class COrphan
{
public:
const CTransaction* ptx;
set<uint256> setDependsOn;
CFeeRate feeRate;
double dPriority;
COrphan(const CTransaction* ptxIn) : ptx(ptxIn), feeRate(0), dPriority(0)
{
}
};
uint64_t nLastBlockTx = 0;
uint64_t nLastBlockSize = 0;
// We want to sort transactions by priority and fee rate, so:
typedef boost::tuple<double, CFeeRate, const CTransaction*> TxPriority;
class TxPriorityCompare
{
bool byFee;
public:
TxPriorityCompare(bool _byFee) : byFee(_byFee) { }
bool operator()(const TxPriority& a, const TxPriority& b)
{
if (byFee)
{
if (a.get<1>() == b.get<1>())
return a.get<0>() < b.get<0>();
return a.get<1>() < b.get<1>();
}
else
{
if (a.get<0>() == b.get<0>())
return a.get<1>() < b.get<1>();
return a.get<0>() < b.get<0>();
}
}
};
4 years ago
extern int8_t ASSETCHAINS_ADAPTIVEPOW;
extern uint32_t ASSETCHAINS_RANDOMX;
extern bool fRandomXDebug;
extern std::string devtax_scriptpub_for_height(uint32_t nHeight);
4 years ago
void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
{
5 years ago
if ( ASSETCHAINS_ADAPTIVEPOW <= 0 )
pblock->nTime = std::max(pindexPrev->GetMedianTimePast()+1, GetTime());
else pblock->nTime = std::max((int64_t)(pindexPrev->nTime+1), GetTime());
// Updating time can change work required on testnet:
5 years ago
if (ASSETCHAINS_ADAPTIVEPOW > 0 || consensusParams.nPowAllowMinDifficultyBlocksAfterHeight != boost::none)
{
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams);
}
}
#include "hush_defs.h"
#include "cc/CCinclude.h"
7 years ago
extern CCriticalSection cs_metrics;
6 years ago
void vcalc_sha256(char deprecated[(256 >> 3) * 2 + 1],uint8_t hash[256 >> 3],uint8_t *src,int32_t len);
8 years ago
uint32_t Mining_start,Mining_height;
int32_t My_notaryid = -1;
3 years ago
int32_t hush_chosennotary(int32_t *notaryidp,int32_t height,uint8_t *pubkey33,uint32_t timestamp);
3 years ago
int32_t hush_baseid(char *origbase);
int32_t hush_longestchain();
int64_t hush_block_unlocktime(uint32_t nHeight);
uint64_t the_commission(const CBlock *block,int32_t height);
int32_t hush_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33, void *ptr);
int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex);
int32_t hush_is_notarytx(const CTransaction& tx);
3 years ago
uint64_t hush_notarypay(CMutableTransaction &txNew, std::vector<int8_t> &NotarizationNotaries, uint32_t timestamp, int32_t height, uint8_t *script, int32_t len);
int32_t hush_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp);
int32_t hush_getnotarizedheight(uint32_t timestamp,int32_t height, uint8_t *script, int32_t len);
CScript hush_mineropret(int32_t nHeight);
bool hush_appendACscriptpub();
8 years ago
CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32_t gpucount, bool isStake)
{
//fprintf(stderr,"%s\n", __func__);
CScript scriptPubKeyIn(_scriptPubKeyIn);
CPubKey pk;
if ( _pk.size() != 33 )
{
pk = CPubKey();
std::vector<std::vector<unsigned char>> vAddrs;
txnouttype txT;
if ( scriptPubKeyIn.size() > 0 && Solver(scriptPubKeyIn, txT, vAddrs))
{
if (txT == TX_PUBKEY)
pk = CPubKey(vAddrs[0]);
}
} else pk = _pk;
uint32_t blocktime; const CChainParams& chainparams = Params();
bool fNotarizationBlock = false; std::vector<int8_t> NotarizationNotaries;
5 years ago
//fprintf(stderr,"%s: create new block with pubkey=%s\n", __func__, HexStr(pk).c_str());
6 years ago
// Create new block
if ( gpucount < 0 )
3 years ago
gpucount = HUSH_MAXGPUCOUNT;
std::unique_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate());
//fprintf(stderr,"%s: created new block template\n", __func__);
if(!pblocktemplate.get())
7 years ago
{
fprintf(stderr,"%s: pblocktemplate.get() failure\n", __func__);
return NULL;
7 years ago
}
CBlock *pblock = &pblocktemplate->block; // pointer for convenience
// -regtest only: allow overriding block.nVersion with
// -blockversion=N to test forking scenarios
if (Params().MineBlocksOnDemand())
pblock->nVersion = GetArg("-blockversion", pblock->nVersion);
// Add dummy coinbase tx as first transaction
pblock->vtx.push_back(CTransaction());
pblocktemplate->vTxFees.push_back(-1); // updated at end
pblocktemplate->vTxSigOps.push_back(-1); // updated at end
//fprintf(stderr,"%s: added dummy coinbase\n", __func__);
// Largest block you're willing to create:
unsigned int nBlockMaxSize = GetArg("-blockmaxsize", MAX_BLOCK_SIZE(chainActive.LastTip()->GetHeight()+1));
// Limit to betweeen 1K and MAX_BLOCK_SIZE-1K for sanity:
nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE(chainActive.LastTip()->GetHeight()+1)-1000), nBlockMaxSize));
// How much of the block should be dedicated to high-priority transactions,
// included regardless of the fees they pay
unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", DEFAULT_BLOCK_PRIORITY_SIZE);
nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize);
// Minimum block size you want to create; block will be filled with free transactions
// until there are no more or the block reaches this size:
unsigned int nBlockMinSize = GetArg("-blockminsize", DEFAULT_BLOCK_MIN_SIZE);
nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize);
//fprintf(stderr,"%s: nBlockMaxSize=%u, nBlockPrioritySize=%u, nBlockMinSize=%u\n", __func__, nBlockMaxSize, nBlockPrioritySize, nBlockMinSize);
2 years ago
// Collect memory pool transactions into the block
CAmount nFees = 0;
6 years ago
uint256 cbHash;
5 years ago
boost::this_thread::interruption_point(); // exit thread before entering locks.
6 years ago
CBlockIndex* pindexPrev = 0;
{
5 years ago
// this should stop create block ever exiting until it has returned something.
boost::this_thread::disable_interruption();
ENTER_CRITICAL_SECTION(cs_main);
ENTER_CRITICAL_SECTION(mempool.cs);
6 years ago
pindexPrev = chainActive.LastTip();
const int nHeight = pindexPrev->GetHeight() + 1;
6 years ago
const Consensus::Params &consensusParams = chainparams.GetConsensus();
uint32_t consensusBranchId = CurrentEpochBranchId(nHeight, consensusParams);
// Sapling NU is always active for height>=1
const bool sapling = nHeight>=1 ? true : false; //NetworkUpgradeActive(nHeight, consensusParams, Consensus::UPGRADE_SAPLING);
const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast();
uint32_t proposedTime = GetTime();
2 years ago
//fprintf(stderr,"%s: nHeight=%d, consensusBranchId=%u, proposedTime=%u\n", __func__, nHeight, consensusBranchId, proposedTime);
2 years ago
if (proposedTime == nMedianTimePast)
{
// too fast or stuck, this addresses the too fast issue, while moving
// forward as quickly as possible
for (int i; i < 100; i++)
{
proposedTime = GetTime();
if (proposedTime == nMedianTimePast)
MilliSleep(10);
}
}
pblock->nTime = GetTime();
// Now we have the block time + height, we can get the active notaries.
int8_t numSN = 0; uint8_t notarypubkeys[64][33] = {0};
if ( ASSETCHAINS_NOTARY_PAY[0] != 0 )
{
// Only use speical miner for notary pay chains.
numSN = hush_notaries(notarypubkeys, nHeight, pblock->nTime);
}
CCoinsViewCache view(pcoinsTip);
6 years ago
uint32_t expired; uint64_t commission;
SaplingMerkleTree sapling_tree;
assert(view.GetSaplingAnchorAt(view.GetBestAnchor(SAPLING), sapling_tree));
// Priority order to process transactions
list<COrphan> vOrphan; // list memory doesn't move
map<uint256, vector<COrphan*> > mapDependers;
bool fPrintPriority = GetBoolArg("-printpriority", false);
// This vector will be sorted into a priority queue:
vector<TxPriority> vecPriority;
6 years ago
vecPriority.reserve(mempool.mapTx.size() + 1);
//fprintf(stderr,"%s: going to add txs from mempool\n", __func__);
Antispam defenses This code is inspired by https://github.com/PirateNetwork/pirate/commit/db292a49ddc13374f43b8c16217e171316be53d7 with various improvements that will be documented below. The largest improvement is that this code will defend against a spammer using shielded inputs (zins) or shielded outputs (zouts) while the Pirate code only defends against zout spam. We wrote a new RPC called z_getstats to study exactly what the distribution of shielded inputs (zins) and shielded outputs (zouts) look like on HUSH mainnet. Sietch will never make a ztx that contains more than 9 zouts and so transactions with 10 or more zouts are extremely rare. They correspond to custom transactions created via code or CLI or mining pool payouts. We allow at most one of these per block. If there are two, one will remain in the mempool and be mined in the subsequent block. Our code is more strict, as Pirate will allow up to 6 of these transactions in a single block. Transactions with many shielded inputs do occur normally when users spend many small shielded unspent outputs (zutxos) in one transaction, but we determined that a cutoff of 50 zins is quite rare. Between blocks 14000000 and 15000000 only 27 ztxs had 50 or more zins, which is 0.03% . We allow at most one of these per block and if there are more, they will wait to be mined in a subsequent block. Also note that a transaction can match both criteria of having large zins and large zouts, so for instance, if there is a transaction with 50 zins and 10 zouts, it counts towards both requirements and no other transactions with >=50 zins or >=10 zouts will be mined in that block. If >=200 transactions with either large zins or large zouts are broadcast to the network it will take at least 200 blocks for them to be mined and so via existing rules for ztx expiration they will expire and be removed from the mempool, since by default all ztxs expire after 200 blocks. Since normal ztxs that match these criteria are very rare, the only case when this might happen is during a spam attack and so the attackers transactions expiring is another part of these defenses. Other improvements are that we log txids of transactions with large zins or zouts and we do not support a command line option to turn this protection off. This forces a potential attacker to compile their own custom code if they want to subvert these protections on their own node and blocks they mine. Similar to Pirate, these changes are not consensus changes but may be made consensus requirements in the future. These protections are not specific to HUSH and are enabled for all HSC's, including DragonX.
8 months ago
// now add transactions from the mempool
int32_t Notarizations = 0; uint64_t txvalue;
Antispam defenses This code is inspired by https://github.com/PirateNetwork/pirate/commit/db292a49ddc13374f43b8c16217e171316be53d7 with various improvements that will be documented below. The largest improvement is that this code will defend against a spammer using shielded inputs (zins) or shielded outputs (zouts) while the Pirate code only defends against zout spam. We wrote a new RPC called z_getstats to study exactly what the distribution of shielded inputs (zins) and shielded outputs (zouts) look like on HUSH mainnet. Sietch will never make a ztx that contains more than 9 zouts and so transactions with 10 or more zouts are extremely rare. They correspond to custom transactions created via code or CLI or mining pool payouts. We allow at most one of these per block. If there are two, one will remain in the mempool and be mined in the subsequent block. Our code is more strict, as Pirate will allow up to 6 of these transactions in a single block. Transactions with many shielded inputs do occur normally when users spend many small shielded unspent outputs (zutxos) in one transaction, but we determined that a cutoff of 50 zins is quite rare. Between blocks 14000000 and 15000000 only 27 ztxs had 50 or more zins, which is 0.03% . We allow at most one of these per block and if there are more, they will wait to be mined in a subsequent block. Also note that a transaction can match both criteria of having large zins and large zouts, so for instance, if there is a transaction with 50 zins and 10 zouts, it counts towards both requirements and no other transactions with >=50 zins or >=10 zouts will be mined in that block. If >=200 transactions with either large zins or large zouts are broadcast to the network it will take at least 200 blocks for them to be mined and so via existing rules for ztx expiration they will expire and be removed from the mempool, since by default all ztxs expire after 200 blocks. Since normal ztxs that match these criteria are very rare, the only case when this might happen is during a spam attack and so the attackers transactions expiring is another part of these defenses. Other improvements are that we log txids of transactions with large zins or zouts and we do not support a command line option to turn this protection off. This forces a potential attacker to compile their own custom code if they want to subvert these protections on their own node and blocks they mine. Similar to Pirate, these changes are not consensus changes but may be made consensus requirements in the future. These protections are not specific to HUSH and are enabled for all HSC's, including DragonX.
8 months ago
uint32_t large_zins = 0; // number of ztxs with large number of inputs in block
uint32_t large_zouts = 0; // number of ztxs with large number of outputs in block
const uint32_t LARGE_ZINS_MAX = 1; // max ztxs with large zins per block
const uint32_t LARGE_ZOUTS_MAX = 1; // max ztxs with large zouts per block
const uint32_t LARGE_ZINS_THRESHOLD = 50; // min number of zins to be considered large
const uint32_t LARGE_ZOUTS_THRESHOLD = 10; // min number of zouts to be considered large
for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin();
Antispam defenses This code is inspired by https://github.com/PirateNetwork/pirate/commit/db292a49ddc13374f43b8c16217e171316be53d7 with various improvements that will be documented below. The largest improvement is that this code will defend against a spammer using shielded inputs (zins) or shielded outputs (zouts) while the Pirate code only defends against zout spam. We wrote a new RPC called z_getstats to study exactly what the distribution of shielded inputs (zins) and shielded outputs (zouts) look like on HUSH mainnet. Sietch will never make a ztx that contains more than 9 zouts and so transactions with 10 or more zouts are extremely rare. They correspond to custom transactions created via code or CLI or mining pool payouts. We allow at most one of these per block. If there are two, one will remain in the mempool and be mined in the subsequent block. Our code is more strict, as Pirate will allow up to 6 of these transactions in a single block. Transactions with many shielded inputs do occur normally when users spend many small shielded unspent outputs (zutxos) in one transaction, but we determined that a cutoff of 50 zins is quite rare. Between blocks 14000000 and 15000000 only 27 ztxs had 50 or more zins, which is 0.03% . We allow at most one of these per block and if there are more, they will wait to be mined in a subsequent block. Also note that a transaction can match both criteria of having large zins and large zouts, so for instance, if there is a transaction with 50 zins and 10 zouts, it counts towards both requirements and no other transactions with >=50 zins or >=10 zouts will be mined in that block. If >=200 transactions with either large zins or large zouts are broadcast to the network it will take at least 200 blocks for them to be mined and so via existing rules for ztx expiration they will expire and be removed from the mempool, since by default all ztxs expire after 200 blocks. Since normal ztxs that match these criteria are very rare, the only case when this might happen is during a spam attack and so the attackers transactions expiring is another part of these defenses. Other improvements are that we log txids of transactions with large zins or zouts and we do not support a command line option to turn this protection off. This forces a potential attacker to compile their own custom code if they want to subvert these protections on their own node and blocks they mine. Similar to Pirate, these changes are not consensus changes but may be made consensus requirements in the future. These protections are not specific to HUSH and are enabled for all HSC's, including DragonX.
8 months ago
mi != mempool.mapTx.end(); ++mi) {
const CTransaction& tx = mi->GetTx();
int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST)
6 years ago
? nMedianTimePast
: pblock->GetBlockTime();
if (tx.IsCoinBase() || !IsFinalTx(tx, nHeight, nLockTimeCutoff) || IsExpiredTx(tx, nHeight))
6 years ago
{
2 years ago
fprintf(stderr,"%s: coinbase.%d finaltx.%d expired.%d\n",__func__, tx.IsCoinBase(),IsFinalTx(tx, nHeight, nLockTimeCutoff),IsExpiredTx(tx, nHeight));
7 years ago
continue;
6 years ago
}
txvalue = tx.GetValueOut();
3 years ago
if ( HUSH_VALUETOOBIG(txvalue) != 0 )
continue;
6 years ago
COrphan* porphan = NULL;
double dPriority = 0;
CAmount nTotalIn = 0;
bool fMissingInputs = false;
bool fNotarization = false;
std::vector<int8_t> TMP_NotarizationNotaries;
//if (tx.IsCoinImport())
//{
// CAmount nValueIn = GetCoinImportValue(tx); // burn amount
// nTotalIn += nValueIn;
// dPriority += (double)nValueIn * 1000; // flat multiplier... max = 1e16.
//} else
{
TMP_NotarizationNotaries.clear();
bool fToCryptoAddress = false;
if ( numSN != 0 && notarypubkeys[0][0] != 0 && hush_is_notarytx(tx) == 1 )
fToCryptoAddress = true;
6 years ago
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
/*
if (tx.IsPegsImport() && txin.prevout.n==10e8)
{
CAmount nValueIn = GetCoinImportValue(tx); // burn amount
nTotalIn += nValueIn;
dPriority += (double)nValueIn * 1000; // flat multiplier... max = 1e16.
continue;
}
*/
6 years ago
// Read prev transaction
if (!view.HaveCoins(txin.prevout.hash))
{
6 years ago
// This should never happen; all transactions in the memory
// pool should connect to either transactions in the chain
// or other transactions in the memory pool.
if (!mempool.mapTx.count(txin.prevout.hash))
{
LogPrintf("ERROR: mempool transaction missing input\n");
// if (fDebug) assert("mempool transaction missing input" == 0);
6 years ago
fMissingInputs = true;
if (porphan)
vOrphan.pop_back();
break;
}
// Has to wait for dependencies
if (!porphan)
{
// Use list for automatic deletion
vOrphan.push_back(COrphan(&tx));
porphan = &vOrphan.back();
}
mapDependers[txin.prevout.hash].push_back(porphan);
porphan->setDependsOn.insert(txin.prevout.hash);
nTotalIn += mempool.mapTx.find(txin.prevout.hash)->GetTx().vout[txin.prevout.n].nValue;
continue;
}
6 years ago
const CCoins* coins = view.AccessCoins(txin.prevout.hash);
assert(coins);
CAmount nValueIn = coins->vout[txin.prevout.n].nValue;
nTotalIn += nValueIn;
int nConf = nHeight - coins->nHeight;
5 years ago
uint8_t *script; int32_t scriptlen; uint256 hash; CTransaction tx1;
// loop over notaries array and extract index of signers.
5 years ago
if ( fToCryptoAddress && GetTransaction(txin.prevout.hash,tx1,hash,false) )
{
5 years ago
for (int8_t i = 0; i < numSN; i++)
{
5 years ago
script = (uint8_t *)&tx1.vout[txin.prevout.n].scriptPubKey[0];
scriptlen = (int32_t)tx1.vout[txin.prevout.n].scriptPubKey.size();
if ( scriptlen == 35 && script[0] == 33 && script[34] == OP_CHECKSIG && memcmp(script+1,notarypubkeys[i],33) == 0 )
5 years ago
{
// We can add the index of each notary to vector, and clear it if this notarization is not valid later on.
TMP_NotarizationNotaries.push_back(i);
5 years ago
}
}
}
6 years ago
dPriority += (double)nValueIn * nConf;
}
if ( numSN != 0 && notarypubkeys[0][0] != 0 && TMP_NotarizationNotaries.size() >= numSN / 5 )
{
// check a notary didnt sign twice (this would be an invalid notarization later on and cause problems)
std::set<int> checkdupes( TMP_NotarizationNotaries.begin(), TMP_NotarizationNotaries.end() );
if ( checkdupes.size() != TMP_NotarizationNotaries.size() )
{
fprintf(stderr, "%s: WTFBBQ! possible notarization is signed multiple times by same notary, passed as normal transaction.\n", __func__);
} else fNotarization = true;
}
nTotalIn += tx.GetShieldedValueIn();
}
6 years ago
if (fMissingInputs) continue;
// Priority is sum(valuein * age) / modified_txsize
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
// fprintf(stderr,"%s: computing priority with nTxSize=%u\n", __func__, nTxSize);
dPriority = tx.ComputePriority(dPriority, nTxSize);
uint256 hash = tx.GetHash();
mempool.ApplyDeltas(hash, dPriority, nTotalIn);
CFeeRate feeRate(nTotalIn-tx.GetValueOut(), nTxSize);
if ( fNotarization ) {
// Special miner for notary pay chains. Can only enter this if numSN/notarypubkeys is set higher up.
5 years ago
if ( tx.vout.size() == 2 && tx.vout[1].nValue == 0 )
5 years ago
{
// Get the OP_RETURN for the notarization
5 years ago
uint8_t *script = (uint8_t *)&tx.vout[1].scriptPubKey[0];
int32_t scriptlen = (int32_t)tx.vout[1].scriptPubKey.size();
if ( script[0] == OP_RETURN )
{
Notarizations++;
if ( Notarizations > 1 )
5 years ago
{
2 years ago
fprintf(stderr, "%s: skipping notarization.%d\n",__func__, Notarizations);
5 years ago
// Any attempted notarization needs to be in its own block!
continue;
}
int32_t notarizedheight = hush_getnotarizedheight(pblock->nTime, nHeight, script, scriptlen);
if ( notarizedheight != 0 )
{
// this is the first one we see, add it to the block as TX1
NotarizationNotaries = TMP_NotarizationNotaries;
dPriority = 1e16;
fNotarizationBlock = true;
//fprintf(stderr, "Notarization %s set to maximum priority\n",hash.ToString().c_str());
}
}
}
} else if ( dPriority == 1e16 ) {
5 years ago
dPriority -= 10;
// make sure notarization is tx[1] in block.
}
if (porphan) {
porphan->dPriority = dPriority;
porphan->feeRate = feeRate;
} else {
vecPriority.push_back(TxPriority(dPriority, feeRate, &(mi->GetTx())));
}
}
// fprintf(stderr,"%s: done adding txs from mempool\n", __func__);
6 years ago
// Collect transactions into block
8 years ago
int64_t interest;
uint64_t nBlockSize = 1000;
uint64_t nBlockTx = 0;
int nBlockSigOps = 100;
bool fSortedByFee = (nBlockPrioritySize <= 0);
TxPriorityCompare comparer(fSortedByFee);
std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
// fprintf(stderr,"%s: compared txs with fSortedByFee=%d\n", __func__, fSortedByFee);
while (!vecPriority.empty()) {
// Take highest priority transaction off the priority queue:
double dPriority = vecPriority.front().get<0>();
CFeeRate feeRate = vecPriority.front().get<1>();
const CTransaction& tx = *(vecPriority.front().get<2>());
// fprintf(stderr,"%s: grabbed first tx from priority queue\n", __func__);
std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer);
// fprintf(stderr,"%s: compared first tx from priority queue\n", __func__);
vecPriority.pop_back();
Antispam defenses This code is inspired by https://github.com/PirateNetwork/pirate/commit/db292a49ddc13374f43b8c16217e171316be53d7 with various improvements that will be documented below. The largest improvement is that this code will defend against a spammer using shielded inputs (zins) or shielded outputs (zouts) while the Pirate code only defends against zout spam. We wrote a new RPC called z_getstats to study exactly what the distribution of shielded inputs (zins) and shielded outputs (zouts) look like on HUSH mainnet. Sietch will never make a ztx that contains more than 9 zouts and so transactions with 10 or more zouts are extremely rare. They correspond to custom transactions created via code or CLI or mining pool payouts. We allow at most one of these per block. If there are two, one will remain in the mempool and be mined in the subsequent block. Our code is more strict, as Pirate will allow up to 6 of these transactions in a single block. Transactions with many shielded inputs do occur normally when users spend many small shielded unspent outputs (zutxos) in one transaction, but we determined that a cutoff of 50 zins is quite rare. Between blocks 14000000 and 15000000 only 27 ztxs had 50 or more zins, which is 0.03% . We allow at most one of these per block and if there are more, they will wait to be mined in a subsequent block. Also note that a transaction can match both criteria of having large zins and large zouts, so for instance, if there is a transaction with 50 zins and 10 zouts, it counts towards both requirements and no other transactions with >=50 zins or >=10 zouts will be mined in that block. If >=200 transactions with either large zins or large zouts are broadcast to the network it will take at least 200 blocks for them to be mined and so via existing rules for ztx expiration they will expire and be removed from the mempool, since by default all ztxs expire after 200 blocks. Since normal ztxs that match these criteria are very rare, the only case when this might happen is during a spam attack and so the attackers transactions expiring is another part of these defenses. Other improvements are that we log txids of transactions with large zins or zouts and we do not support a command line option to turn this protection off. This forces a potential attacker to compile their own custom code if they want to subvert these protections on their own node and blocks they mine. Similar to Pirate, these changes are not consensus changes but may be made consensus requirements in the future. These protections are not specific to HUSH and are enabled for all HSC's, including DragonX.
8 months ago
if(tx.vShieldedSpend.size() >= LARGE_ZINS_THRESHOLD && large_zins >= LARGE_ZINS_MAX) {
LogPrintf("%s: skipping ztx %s with %d zins because there are already %d ztxs with large zins\n",
__func__, tx.GetHash().ToString().c_str(), tx.vShieldedSpend.size(), LARGE_ZINS_MAX);
continue;
}
if(tx.vShieldedOutput.size() >= LARGE_ZOUTS_THRESHOLD && large_zouts >= LARGE_ZOUTS_MAX) {
LogPrintf("%s: skipping ztx %s with %d zouts because there are already %d ztxs with large zouts\n",
__func__, tx.GetHash().ToString().c_str(), tx.vShieldedOutput.size(), LARGE_ZOUTS_MAX);
continue;
}
// Size limits
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
// fprintf(stderr,"%s: nTxSize = %u\n", __func__, nTxSize);
// Opret spam limits
if (mapArgs.count("-opretmintxfee"))
{
CAmount n = 0;
CFeeRate opretMinFeeRate;
if (ParseMoney(mapArgs["-opretmintxfee"], n) && n > 0)
opretMinFeeRate = CFeeRate(n);
else
opretMinFeeRate = CFeeRate(400000); // default opretMinFeeRate (1 HUSH per 250 Kb = 0.004 per 1 Kb = 400000 puposhis per 1 Kb)
bool fSpamTx = false;
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
unsigned int nTxOpretSize = 0;
// calc total oprets size
BOOST_FOREACH(const CTxOut& txout, tx.vout) {
if (txout.scriptPubKey.IsOpReturn()) {
CScript::const_iterator it = txout.scriptPubKey.begin() + 1;
opcodetype op;
std::vector<uint8_t> opretData;
if (txout.scriptPubKey.GetOp(it, op, opretData)) {
//std::cerr << HexStr(opretData.begin(), opretData.end()) << std::endl;
nTxOpretSize += opretData.size();
}
}
}
if ((nTxOpretSize > 256) && (feeRate < opretMinFeeRate)) fSpamTx = true;
// std::cerr << tx.GetHash().ToString() << " nTxSize." << nTxSize << " nTxOpretSize." << nTxOpretSize << " feeRate." << feeRate.ToString() << " opretMinFeeRate." << opretMinFeeRate.ToString() << " fSpamTx." << fSpamTx << std::endl;
if (fSpamTx) continue;
// std::cerr << tx.GetHash().ToString() << " vecPriority.size() = " << vecPriority.size() << std::endl;
}
6 years ago
if (nBlockSize + nTxSize >= nBlockMaxSize-512) // room for extra autotx
6 years ago
{
2 years ago
fprintf(stderr,"%s: nBlockSize %d + %d nTxSize >= %d nBlockMaxSize\n",__func__, (int32_t)nBlockSize,(int32_t)nTxSize,(int32_t)nBlockMaxSize);
continue;
6 years ago
}
// Legacy limits on sigOps:
unsigned int nTxSigOps = GetLegacySigOpCount(tx);
6 years ago
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS-1)
6 years ago
{
//fprintf(stderr,"A nBlockSigOps %d + %d nTxSigOps >= %d MAX_BLOCK_SIGOPS-1\n",(int32_t)nBlockSigOps,(int32_t)nTxSigOps,(int32_t)MAX_BLOCK_SIGOPS);
continue;
6 years ago
}
2 years ago
// fprintf(stderr,"%s: looking to see if we need to skip any fee=0 txs\n", __func__);
2 years ago
// Skip free transactions if we're past the minimum block size:
const uint256& hash = tx.GetHash();
double dPriorityDelta = 0;
CAmount nFeeDelta = 0;
mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
if (fSortedByFee && (dPriorityDelta <= 0) && (nFeeDelta <= 0) && (feeRate < ::minRelayTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
6 years ago
{
2 years ago
fprintf(stderr,"%s: fee rate skip\n", __func__);
continue;
6 years ago
}
// Prioritize by fee once past the priority size or we run out of high-priority transactions
if (!fSortedByFee &&
((nBlockSize + nTxSize >= nBlockPrioritySize) || !AllowFree(dPriority)))
{
fSortedByFee = true;
comparer = TxPriorityCompare(fSortedByFee);
std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
}
if (!view.HaveInputs(tx))
6 years ago
{
//fprintf(stderr,"dont have inputs\n");
continue;
6 years ago
}
CAmount nTxFees = view.GetValueIn(chainActive.LastTip()->GetHeight(),&interest,tx,chainActive.LastTip()->nTime)-tx.GetValueOut();
nTxSigOps += GetP2SHSigOpCount(tx, view);
6 years ago
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS-1)
6 years ago
{
//fprintf(stderr,"B nBlockSigOps %d + %d nTxSigOps >= %d MAX_BLOCK_SIGOPS-1\n",(int32_t)nBlockSigOps,(int32_t)nTxSigOps,(int32_t)MAX_BLOCK_SIGOPS);
continue;
6 years ago
}
// Note that flags: we don't want to set mempool/IsStandard()
// policy here, but we still have to ensure that the block we
// create only contains transactions that are valid in new blocks.
CValidationState state;
PrecomputedTransactionData txdata(tx);
if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId))
6 years ago
{
2 years ago
fprintf(stderr,"%s: ContextualCheckInputs failure\n",__func__);
continue;
6 years ago
}
UpdateCoins(tx, view, nHeight);
BOOST_FOREACH(const OutputDescription &outDescription, tx.vShieldedOutput) {
sapling_tree.append(outDescription.cm);
}
// Added
pblock->vtx.push_back(tx);
pblocktemplate->vTxFees.push_back(nTxFees);
pblocktemplate->vTxSigOps.push_back(nTxSigOps);
nBlockSize += nTxSize;
++nBlockTx;
nBlockSigOps += nTxSigOps;
nFees += nTxFees;
Antispam defenses This code is inspired by https://github.com/PirateNetwork/pirate/commit/db292a49ddc13374f43b8c16217e171316be53d7 with various improvements that will be documented below. The largest improvement is that this code will defend against a spammer using shielded inputs (zins) or shielded outputs (zouts) while the Pirate code only defends against zout spam. We wrote a new RPC called z_getstats to study exactly what the distribution of shielded inputs (zins) and shielded outputs (zouts) look like on HUSH mainnet. Sietch will never make a ztx that contains more than 9 zouts and so transactions with 10 or more zouts are extremely rare. They correspond to custom transactions created via code or CLI or mining pool payouts. We allow at most one of these per block. If there are two, one will remain in the mempool and be mined in the subsequent block. Our code is more strict, as Pirate will allow up to 6 of these transactions in a single block. Transactions with many shielded inputs do occur normally when users spend many small shielded unspent outputs (zutxos) in one transaction, but we determined that a cutoff of 50 zins is quite rare. Between blocks 14000000 and 15000000 only 27 ztxs had 50 or more zins, which is 0.03% . We allow at most one of these per block and if there are more, they will wait to be mined in a subsequent block. Also note that a transaction can match both criteria of having large zins and large zouts, so for instance, if there is a transaction with 50 zins and 10 zouts, it counts towards both requirements and no other transactions with >=50 zins or >=10 zouts will be mined in that block. If >=200 transactions with either large zins or large zouts are broadcast to the network it will take at least 200 blocks for them to be mined and so via existing rules for ztx expiration they will expire and be removed from the mempool, since by default all ztxs expire after 200 blocks. Since normal ztxs that match these criteria are very rare, the only case when this might happen is during a spam attack and so the attackers transactions expiring is another part of these defenses. Other improvements are that we log txids of transactions with large zins or zouts and we do not support a command line option to turn this protection off. This forces a potential attacker to compile their own custom code if they want to subvert these protections on their own node and blocks they mine. Similar to Pirate, these changes are not consensus changes but may be made consensus requirements in the future. These protections are not specific to HUSH and are enabled for all HSC's, including DragonX.
8 months ago
if(tx.vShieldedOutput.size() >= LARGE_ZOUTS_THRESHOLD) {
large_zouts++;
LogPrintf("%s: txid=%s has large zouts=%d (%d large zouts in block)\n", __func__, tx.GetHash().ToString().c_str(),
tx.vShieldedOutput.size(), large_zouts );
}
if(tx.vShieldedSpend.size() >= LARGE_ZINS_THRESHOLD) {
large_zins++;
LogPrintf("%s: txid=%s has large zins=%d (%d large zins in block)\n", __func__, tx.GetHash().ToString().c_str(),
Antispam defenses This code is inspired by https://github.com/PirateNetwork/pirate/commit/db292a49ddc13374f43b8c16217e171316be53d7 with various improvements that will be documented below. The largest improvement is that this code will defend against a spammer using shielded inputs (zins) or shielded outputs (zouts) while the Pirate code only defends against zout spam. We wrote a new RPC called z_getstats to study exactly what the distribution of shielded inputs (zins) and shielded outputs (zouts) look like on HUSH mainnet. Sietch will never make a ztx that contains more than 9 zouts and so transactions with 10 or more zouts are extremely rare. They correspond to custom transactions created via code or CLI or mining pool payouts. We allow at most one of these per block. If there are two, one will remain in the mempool and be mined in the subsequent block. Our code is more strict, as Pirate will allow up to 6 of these transactions in a single block. Transactions with many shielded inputs do occur normally when users spend many small shielded unspent outputs (zutxos) in one transaction, but we determined that a cutoff of 50 zins is quite rare. Between blocks 14000000 and 15000000 only 27 ztxs had 50 or more zins, which is 0.03% . We allow at most one of these per block and if there are more, they will wait to be mined in a subsequent block. Also note that a transaction can match both criteria of having large zins and large zouts, so for instance, if there is a transaction with 50 zins and 10 zouts, it counts towards both requirements and no other transactions with >=50 zins or >=10 zouts will be mined in that block. If >=200 transactions with either large zins or large zouts are broadcast to the network it will take at least 200 blocks for them to be mined and so via existing rules for ztx expiration they will expire and be removed from the mempool, since by default all ztxs expire after 200 blocks. Since normal ztxs that match these criteria are very rare, the only case when this might happen is during a spam attack and so the attackers transactions expiring is another part of these defenses. Other improvements are that we log txids of transactions with large zins or zouts and we do not support a command line option to turn this protection off. This forces a potential attacker to compile their own custom code if they want to subvert these protections on their own node and blocks they mine. Similar to Pirate, these changes are not consensus changes but may be made consensus requirements in the future. These protections are not specific to HUSH and are enabled for all HSC's, including DragonX.
8 months ago
tx.vShieldedSpend.size(), large_zins );
}
if (fPrintPriority)
{
8 years ago
LogPrintf("priority %.1f fee %s txid %s\n",dPriority, feeRate.ToString(), tx.GetHash().ToString());
}
// Add transactions that depend on this one to the priority queue
if (mapDependers.count(hash))
{
BOOST_FOREACH(COrphan* porphan, mapDependers[hash])
{
if (!porphan->setDependsOn.empty())
{
porphan->setDependsOn.erase(hash);
if (porphan->setDependsOn.empty())
{
vecPriority.push_back(TxPriority(porphan->dPriority, porphan->feeRate, porphan->ptx));
std::push_heap(vecPriority.begin(), vecPriority.end(), comparer);
}
}
}
}
}
nLastBlockTx = nBlockTx;
nLastBlockSize = nBlockSize;
// fprintf(stderr,"%s: nLastBlockTx=%lu , nLastBlockSize=%lu\n", __func__, nLastBlockTx, nLastBlockSize);
5 years ago
if ( ASSETCHAINS_ADAPTIVEPOW <= 0 )
blocktime = 1 + std::max(pindexPrev->GetMedianTimePast()+1, GetTime());
else blocktime = 1 + std::max((int64_t)(pindexPrev->nTime+1), GetTime());
6 years ago
//pblock->nTime = blocktime + 1;
2 years ago
// fprintf(stderr,"%s: calling GetNextWorkRequired\n", __func__);
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus());
LogPrintf("CreateNewBlock(): total size %u blocktime.%u nBits.%08x\n", nBlockSize,blocktime,pblock->nBits);
5 years ago
// Create coinbase tx
6 years ago
CMutableTransaction txNew = CreateNewContextualCMutableTransaction(consensusParams, nHeight);
txNew.vin.resize(1);
txNew.vin[0].prevout.SetNull();
6 years ago
txNew.vin[0].scriptSig = CScript() << nHeight << OP_0;
8 years ago
txNew.vout.resize(1);
txNew.vout[0].scriptPubKey = scriptPubKeyIn;
6 years ago
txNew.vout[0].nValue = GetBlockSubsidy(nHeight,consensusParams) + nFees;
2 years ago
// fprintf(stderr,"%s: mine ht.%d with %.8f\n",__func__,nHeight,(double)txNew.vout[0].nValue/COIN);
txNew.nExpiryHeight = 0;
5 years ago
if ( ASSETCHAINS_ADAPTIVEPOW <= 0 )
txNew.nLockTime = std::max(pindexPrev->GetMedianTimePast()+1, GetTime());
else txNew.nLockTime = std::max((int64_t)(pindexPrev->nTime+1), GetTime());
6 years ago
pblock->vtx[0] = txNew;
// Create a local variable instead of modifying the global ASSETCHAINS_SCRIPTPUB
auto assetchains_scriptpub = devtax_scriptpub_for_height(nHeight);
if ( nHeight > 1 && (ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || assetchains_scriptpub.size() > 1) && (ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_FOUNDERS_REWARD != 0) && (commission= the_commission((CBlock*)&pblocktemplate->block,(int32_t)nHeight)) != 0 )
8 years ago
{
6 years ago
int32_t i; uint8_t *ptr;
6 years ago
txNew.vout.resize(2);
txNew.vout[1].nValue = commission;
if ( assetchains_scriptpub.size() > 1 )
{
static bool didinit = false;
if ( !didinit && nHeight > HUSH_EARLYTXID_HEIGHT && HUSH_EARLYTXID != zeroid && hush_appendACscriptpub() )
{
fprintf(stderr, "appended ccopreturn to assetchains_scriptpub.%s\n", assetchains_scriptpub.c_str());
didinit = true;
}
5 years ago
//fprintf(stderr,"mine to -ac_script\n");
//txNew.vout[1].scriptPubKey = CScript() << ParseHex();
int32_t len = strlen(assetchains_scriptpub.c_str());
len >>= 1;
txNew.vout[1].scriptPubKey.resize(len);
ptr = (uint8_t *)&txNew.vout[1].scriptPubKey[0];
decode_hex(ptr,len,(char *)assetchains_scriptpub.c_str());
} else {
6 years ago
txNew.vout[1].scriptPubKey.resize(35);
ptr = (uint8_t *)&txNew.vout[1].scriptPubKey[0];
6 years ago
ptr[0] = 33;
for (i=0; i<33; i++)
5 years ago
{
6 years ago
ptr[i+1] = ASSETCHAINS_OVERRIDE_PUBKEY33[i];
5 years ago
//fprintf(stderr,"%02x",ptr[i+1]);
}
6 years ago
ptr[34] = OP_CHECKSIG;
5 years ago
//fprintf(stderr," set ASSETCHAINS_OVERRIDE_PUBKEY33 into vout[1]\n");
6 years ago
}
6 years ago
//printf("autocreate commision vout\n");
} else if ( (uint64_t)(txNew.vout[0].nValue) >= ASSETCHAINS_TIMELOCKGTE) {
fprintf(stderr,"timelocked chains not supported in this code!\n");
LEAVE_CRITICAL_SECTION(cs_main);
LEAVE_CRITICAL_SECTION(mempool.cs);
return(0);
} else if ( fNotarizationBlock && ASSETCHAINS_NOTARY_PAY[0] != 0 && pblock->vtx[1].vout.size() == 2 && pblock->vtx[1].vout[1].nValue == 0 ) {
// Get the OP_RETURN for the notarization
uint8_t *script = (uint8_t *)&pblock->vtx[1].vout[1].scriptPubKey[0];
int32_t scriptlen = (int32_t)pblock->vtx[1].vout[1].scriptPubKey.size();
if ( script[0] == OP_RETURN )
{
3 years ago
uint64_t totalsats = hush_notarypay(txNew, NotarizationNotaries, pblock->nTime, nHeight, script, scriptlen);
if ( totalsats == 0 )
{
fprintf(stderr, "Could not create notary payment, trying again.\n");
if ( SMART_CHAIN_SYMBOL[0] == 0 || (SMART_CHAIN_SYMBOL[0] != 0 && !isStake) )
{
LEAVE_CRITICAL_SECTION(cs_main);
LEAVE_CRITICAL_SECTION(mempool.cs);
}
return(0);
}
5 years ago
//fprintf(stderr, "Created notary payment coinbase totalsat.%lu\n",totalsats);
} else fprintf(stderr, "vout 2 of notarization is not OP_RETURN scriptlen.%i\n", scriptlen);
5 years ago
}
5 years ago
if ( ASSETCHAINS_CBOPRET != 0 )
{
5 years ago
int32_t numv = (int32_t)txNew.vout.size();
txNew.vout.resize(numv+1);
txNew.vout[numv].nValue = 0;
txNew.vout[numv].scriptPubKey = hush_mineropret(nHeight);
5 years ago
//printf("autocreate commision/cbopret.%lld vout[%d]\n",(long long)ASSETCHAINS_CBOPRET,(int32_t)txNew.vout.size());
5 years ago
}
pblock->vtx[0] = txNew;
pblocktemplate->vTxFees[0] = -nFees;
// if not staking, setup nonce, otherwise, leave it alone
if (!isStake || ASSETCHAINS_LWMAPOS == 0)
{
2 years ago
// Randomize nonce
arith_uint256 nonce = UintToArith256(GetRandHash());
// Clear the top 16 and bottom 16 or 24 bits (for local use as thread flags and counters)
nonce <<= ASSETCHAINS_NONCESHIFT[ASSETCHAINS_ALGO];
nonce >>= 16;
pblock->nNonce = ArithToUint256(nonce);
}
// Fill in header
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
pblock->hashFinalSaplingRoot = sapling_tree.root();
// all PoS chains need this data in the block at all times
if ( ASSETCHAINS_LWMAPOS || ASSETCHAINS_STAKED == 0 || HUSH_MININGTHREADS > 0 )
6 years ago
{
UpdateTime(pblock, Params().GetConsensus(), pindexPrev);
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus());
6 years ago
}
pblock->nSolution.clear();
pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]);
/*
if ( SMART_CHAIN_SYMBOL[0] == 0 && IS_HUSH_NOTARY != 0 && My_notaryid >= 0 )
8 years ago
{
4 years ago
uint32_t r; CScript opret; void **ptr=0;
6 years ago
CMutableTransaction txNotary = CreateNewContextualCMutableTransaction(Params().GetConsensus(), chainActive.Height() + 1);
6 years ago
if ( pblock->nTime < pindexPrev->nTime+60 )
pblock->nTime = pindexPrev->nTime + 60;
if ( gpucount < 33 )
{
6 years ago
uint8_t tmpbuffer[40]; uint32_t r; int32_t n=0; uint256 randvals;
memcpy(&tmpbuffer[n],&My_notaryid,sizeof(My_notaryid)), n += sizeof(My_notaryid);
memcpy(&tmpbuffer[n],&Mining_height,sizeof(Mining_height)), n += sizeof(Mining_height);
memcpy(&tmpbuffer[n],&pblock->hashPrevBlock,sizeof(pblock->hashPrevBlock)), n += sizeof(pblock->hashPrevBlock);
6 years ago
vcalc_sha256(0,(uint8_t *)&randvals,tmpbuffer,n);
6 years ago
memcpy(&r,&randvals,sizeof(r));
pblock->nTime += (r % (33 - gpucount)*(33 - gpucount));
}
if ( hush_notaryvin(txNotary,NOTARY_PUBKEY33,ptr) > 0 )
6 years ago
{
CAmount txfees = 5000;
6 years ago
pblock->vtx.push_back(txNotary);
pblocktemplate->vTxFees.push_back(txfees);
pblocktemplate->vTxSigOps.push_back(GetLegacySigOpCount(txNotary));
nFees += txfees;
pblocktemplate->vTxFees[0] = -nFees;
6 years ago
//*(uint64_t *)(&pblock->vtx[0].vout[0].nValue) += txfees;
6 years ago
//fprintf(stderr,"added notaryvin\n");
2 years ago
} else {
6 years ago
fprintf(stderr,"error adding notaryvin, need to create 0.0001 utxos\n");
if ( SMART_CHAIN_SYMBOL[0] == 0 || (SMART_CHAIN_SYMBOL[0] != 0 && !isStake) )
{
LEAVE_CRITICAL_SECTION(cs_main);
LEAVE_CRITICAL_SECTION(mempool.cs);
}
6 years ago
return(0);
}
} else */
if ( ASSETCHAINS_CC == 0 && pindexPrev != 0 && ASSETCHAINS_STAKED == 0 && (SMART_CHAIN_SYMBOL[0] != 0 || IS_HUSH_NOTARY == 0 || My_notaryid < 0) )
8 years ago
{
CValidationState state;
2 years ago
//fprintf(stderr,"%s: check validity\n", __func__);
if ( !TestBlockValidity(state, *pblock, pindexPrev, false, false)) // invokes CC checks
{
if ( (SMART_CHAIN_SYMBOL[0] != 0 && !isStake) )
5 years ago
{
LEAVE_CRITICAL_SECTION(cs_main);
LEAVE_CRITICAL_SECTION(mempool.cs);
}
fprintf(stderr,"%s: TestBlockValidity failed!\n", __func__);
//throw std::runtime_error("CreateNewBlock(): TestBlockValidity failed"); // crashes the node, moved to GetBlockTemplate and issue return.
return(0);
}
//fprintf(stderr,"valid\n");
8 years ago
}
}
if ( (SMART_CHAIN_SYMBOL[0] != 0 && !isStake) )
{
LEAVE_CRITICAL_SECTION(cs_main);
LEAVE_CRITICAL_SECTION(mempool.cs);
}
// fprintf(stderr,"%s: done\n", __func__);
return pblocktemplate.release();
}
// Internal miner
#ifdef ENABLE_MINING
void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce)
{
//fprintf(stderr,"RandomXMiner: %s with nExtraNonce=%u\n", __func__, nExtraNonce);
// Update nExtraNonce
static uint256 hashPrevBlock;
if (hashPrevBlock != pblock->hashPrevBlock)
{
nExtraNonce = 0;
hashPrevBlock = pblock->hashPrevBlock;
}
++nExtraNonce;
unsigned int nHeight = pindexPrev->GetHeight()+1; // Height first in coinbase required for block.version=2
CMutableTransaction txCoinbase(pblock->vtx[0]);
txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)) + COINBASE_FLAGS;
assert(txCoinbase.vin[0].scriptSig.size() <= 100);
pblock->vtx[0] = txCoinbase;
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
}
#ifdef ENABLE_WALLET
// Internal miner
CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int32_t nHeight, int32_t gpucount, bool isStake)
{
CPubKey pubkey; CScript scriptPubKey; uint8_t *script,*ptr; int32_t i,len;
2 years ago
// fprintf(stderr,"%s: with nHeight=%d\n", __func__, nHeight);
// Create a local variable instead of modifying the global assetchains_scriptpub
auto assetchains_scriptpub = devtax_scriptpub_for_height(nHeight);
if ( nHeight == 1 && ASSETCHAINS_COMMISSION != 0 && assetchains_scriptpub[assetchains_scriptpub.back()] != 49 && assetchains_scriptpub[assetchains_scriptpub.back()-1] != 51 )
{
if ( ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 )
5 years ago
{
pubkey = ParseHex(ASSETCHAINS_OVERRIDE_PUBKEY);
5 years ago
scriptPubKey = CScript() << ParseHex(HexStr(pubkey)) << OP_CHECKSIG;
2 years ago
// fprintf(stderr,"%s: with pubkey=%s\n", __func__, HexStr(pubkey).c_str() );
} else {
len = strlen(assetchains_scriptpub.c_str());
len >>= 1;
6 years ago
scriptPubKey.resize(len);
ptr = (uint8_t *)&scriptPubKey[0];
decode_hex(ptr,len,(char *)assetchains_scriptpub.c_str());
}
} else if ( USE_EXTERNAL_PUBKEY != 0 ) {
8 years ago
//fprintf(stderr,"use notary pubkey\n");
5 years ago
pubkey = ParseHex(NOTARY_PUBKEY);
5 years ago
scriptPubKey = CScript() << ParseHex(HexStr(pubkey)) << OP_CHECKSIG;
} else {
7 years ago
{
// Support mining with -disablewallet and minetolocalwallet=0
if (!GetBoolArg("-disablewallet", false)) {
// wallet enabled
if (!reservekey.GetReservedKey(pubkey))
return NULL;
scriptPubKey.clear();
scriptPubKey = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
} else {
// wallet disabled
CTxDestination dest = DecodeDestination(GetArg("-mineraddress", ""));
char destaddr[65];
if (IsValidDestination(dest)) {
// CKeyID keyID = boost::get<CKeyID>(dest);
// scriptPubKey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
scriptPubKey = GetScriptForDestination(dest);
Getscriptaddress(destaddr,scriptPubKey);
fprintf(stderr,"%s: wallet disabled with mineraddress=%s\n", __func__, destaddr);
} else {
return NULL;
}
}
}
}
2 years ago
// fprintf(stderr,"%s: calling CreateNewBlock\n", __func__);
return CreateNewBlock(pubkey, scriptPubKey, gpucount, isStake);
}
void hush_sendmessage(int32_t minpeers,int32_t maxpeers,const char *message,std::vector<uint8_t> payload)
5 years ago
{
int32_t numsent = 0;
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
{
if ( pnode->hSocket == INVALID_SOCKET )
continue;
if ( numsent < minpeers || (rand() % 10) == 0 )
{
//fprintf(stderr,"pushmessage\n");
5 years ago
pnode->PushMessage(message,payload);
if ( numsent++ > maxpeers )
break;
}
}
}
static bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey)
#else
static bool ProcessBlockFound(CBlock* pblock)
#endif // ENABLE_WALLET
{
LogPrintf("%s\n", pblock->ToString());
LogPrintf("generated %s height.%d\n", FormatMoney(pblock->vtx[0].vout[0].nValue),chainActive.LastTip()->GetHeight()+1);
// Found a solution
{
if (pblock->hashPrevBlock != chainActive.LastTip()->GetBlockHash())
7 years ago
{
uint256 hash; int32_t i;
hash = pblock->hashPrevBlock;
7 years ago
for (i=31; i>=0; i--)
7 years ago
fprintf(stderr,"%02x",((uint8_t *)&hash)[i]);
7 years ago
fprintf(stderr," <- prev (stale)\n");
hash = chainActive.LastTip()->GetBlockHash();
7 years ago
for (i=31; i>=0; i--)
7 years ago
fprintf(stderr,"%02x",((uint8_t *)&hash)[i]);
7 years ago
fprintf(stderr," <- chainTip (stale)\n");
return error("HushMiner: generated block is stale");
7 years ago
}
}
// Inform about the new block
GetMainSignals().BlockFound(pblock->GetHash());
#ifdef ENABLE_WALLET
// Remove key from key pool
if ( IS_HUSH_NOTARY == 0 )
7 years ago
{
if (GetArg("-mineraddress", "").empty()) {
// Remove key from key pool
reservekey.KeepKey();
}
}
#endif
6 years ago
//fprintf(stderr,"process new block\n");
// Process this block the same as if we had received it from another node
CValidationState state;
if (!ProcessNewBlock(1,chainActive.LastTip()->GetHeight()+1,state, NULL, pblock, true, NULL))
return error("HushMiner: ProcessNewBlock, block not accepted");
TrackMinedBlock(pblock->GetHash());
return true;
}
3 years ago
int32_t hush_baseid(char *origbase);
3 years ago
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;
3 years ago
extern int32_t HUSH_LASTMINED,HUSH_INSYNC;
7 years ago
int32_t roundrobin_delay;
arith_uint256 HASHTarget,HASHTarget_POW;
8 years ago
// wait for peers to connect
void waitForPeers(const CChainParams &chainparams)
{
if (chainparams.MiningRequiresPeers())
{
bool fvNodesEmpty;
{
boost::this_thread::interruption_point();
LOCK(cs_vNodes);
fvNodesEmpty = vNodes.empty();
}
if (fvNodesEmpty || IsNotInSync())
{
int loops = 0, blockDiff = 0, newDiff = 0;
do {
if (fvNodesEmpty)
{
MilliSleep(1000 + rand() % 4000);
boost::this_thread::interruption_point();
LOCK(cs_vNodes);
fvNodesEmpty = vNodes.empty();
loops = 0;
blockDiff = 0;
}
if ((newDiff = IsNotInSync()) > 1)
{
if (blockDiff != newDiff)
{
blockDiff = newDiff;
}
else
{
if (++loops <= 10)
{
MilliSleep(1000);
}
else break;
}
}
} while (fvNodesEmpty || IsNotInSync());
MilliSleep(100 + rand() % 400);
}
}
}
#ifdef ENABLE_WALLET
CBlockIndex *get_chainactive(int32_t height)
{
if ( chainActive.LastTip() != 0 )
{
if ( height <= chainActive.LastTip()->GetHeight() )
{
LOCK(cs_main);
return(chainActive[height]);
}
// else fprintf(stderr,"get_chainactive height %d > active.%d\n",height,chainActive.Tip()->GetHeight());
}
//fprintf(stderr,"get_chainactive null chainActive.Tip() height %d\n",height);
return(0);
}
#endif
int32_t gotinvalid;
class RandomXSolverCanceledException : public std::exception
{
virtual const char* what() const throw() {
return "RandomX solver was canceled";
}
};
enum RandomXSolverCancelCheck
{
Reason1,
Reason2
};
int GetRandomXInterval() { return GetArg("-ac_randomx_interval",1024); }
int GetRandomXBlockLag() { return GetArg("-ac_randomx_lag", 64); }
#ifdef ENABLE_WALLET
void static RandomXMiner(CWallet *pwallet)
#else
void static RandomXMiner()
#endif
{
LogPrintf("HushRandomXMiner started\n");
SetThreadPriority(THREAD_PRIORITY_LOWEST);
RenameThread("hush-randomx");
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;
int32_t gpucount=HUSH_MAXGPUCOUNT;
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();
2 years ago
randomx_flags flags = randomx_get_flags();
flags |= RANDOMX_FLAG_FULL_MEM;
randomx_cache *randomxCache = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES | RANDOMX_FLAG_SECURE );
2 years ago
if (randomxCache == NULL) {
LogPrintf("RandomX cache is null, trying without large pages...\n");
randomxCache = randomx_alloc_cache(flags | RANDOMX_FLAG_SECURE);
if (randomxCache == NULL) {
LogPrintf("RandomX cache is null, trying without secure...\n");
}
randomxCache = randomx_alloc_cache(flags);
if (randomxCache == NULL) {
LogPrintf("RandomX cache is null, cannot mine!\n");
}
2 years ago
}
2 years ago
rxdebug("%s: created randomx flags + cache\n");
randomx_dataset *randomxDataset = randomx_alloc_dataset(flags);
rxdebug("%s: created dataset\n");
if( randomxDataset == nullptr) {
LogPrintf("%s: allocating randomx dataset failed!\n", __func__);
return;
}
auto datasetItemCount = randomx_dataset_item_count();
rxdebug("%s: dataset items=%lu\n", datasetItemCount);
char randomxHash[RANDOMX_HASH_SIZE];
rxdebug("%s: created randomxHash of size %d\n", RANDOMX_HASH_SIZE);
char randomxKey[82]; // randomx spec says keysize of >60 bytes is implementation-specific
// initial randomx key is unique to every Hush Smart Chain, and has at least 9 bytes (2^9=128 bits) of entropy
// since magic is 4 bytes, rpc port is 4 bytes and smart chain symbol must be at least 1 character long
snprintf(randomxKey, 81, "%08x%s%08x", ASSETCHAINS_MAGIC, SMART_CHAIN_SYMBOL, ASSETCHAINS_RPCPORT);
// With the defaults of 1024 and 64
// the key block will change every ~21.3 hours with a 75s block time
// and every ~17 hours with the default 60s block time for HSCs
static int randomxInterval = GetRandomXInterval();
// This lag is 80 mins for 75s blocktime and 64 mins for 60s (default) blocktime for HSCs
static int randomxBlockLag = GetRandomXBlockLag();
randomx_vm *myVM = nullptr;
try {
// fprintf(stderr,"RandomXMiner: mining %s with randomx\n",SMART_CHAIN_SYMBOL);
rxdebug("%s: mining %s with randomx\n", SMART_CHAIN_SYMBOL);
while (true)
{
// fprintf(stderr,"RandomXMiner: beginning mining loop on %s with nExtraNonce=%u\n",SMART_CHAIN_SYMBOL, nExtraNonce);
rxdebug("%s: start mining loop on %s with nExtraNonce=%u\n", SMART_CHAIN_SYMBOL, nExtraNonce);
if (chainparams.MiningRequiresPeers()) {
//if ( ASSETCHAINS_SEED != 0 && chainActive.LastTip()->GetHeight() < 100 )
// break;
// Busy-wait for the network to come online so we don't waste time mining
// on an obsolete chain. In regtest mode we expect to fly solo.
miningTimer.stop();
do {
bool fvNodesEmpty;
{
//LOCK(cs_vNodes);
fvNodesEmpty = vNodes.empty();
}
if (!fvNodesEmpty )//&& !IsInitialBlockDownload())
break;
MilliSleep(15000);
//fprintf(stderr,"fvNodesEmpty %d IsInitialBlockDownload(%s) %d\n",(int32_t)fvNodesEmpty,SMART_CHAIN_SYMBOL,(int32_t)IsInitialBlockDownload());
} while (true);
//fprintf(stderr,"%s Found peers\n",SMART_CHAIN_SYMBOL);
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);
}
2 years ago
// fprintf(stderr,"RandomXMiner: using initial key with interval=%d and lag=%d\n", randomxInterval, randomxBlockLag);
rxdebug("%s: using initial key, interval=%d, lag=%d, Mining_height=%u\n", randomxInterval, randomxBlockLag, Mining_height);
// Use the initial key at the start of the chain, until the first key block
if( (Mining_height) < randomxInterval + randomxBlockLag) {
randomx_init_cache(randomxCache, &randomxKey, sizeof randomxKey);
rxdebug("%s: initialized cache with initial key\n");
} else {
rxdebug("%s: calculating keyHeight with randomxInterval=%d\n", randomxInterval);
// At heights between intervals, we use the same block key and wait randomxBlockLag blocks until changing
int keyHeight = ((Mining_height - randomxBlockLag) / randomxInterval) * randomxInterval;
uint256 randomxBlockKey = chainActive[keyHeight]->GetBlockHash();
2 years ago
randomx_init_cache(randomxCache, &randomxBlockKey, sizeof randomxBlockKey);
rxdebug("%s: initialized cache with keyHeight=%d, randomxBlockKey=%s\n", keyHeight, randomxBlockKey.ToString().c_str());
}
const int initThreadCount = std::thread::hardware_concurrency();
if(initThreadCount > 1) {
rxdebug("%s: initializing dataset with %d threads\n", initThreadCount);
std::vector<std::thread> threads;
uint32_t startItem = 0;
auto perThread = datasetItemCount / initThreadCount;
auto remainder = datasetItemCount % initThreadCount;
for (int i = 0; i < initThreadCount; ++i) {
auto count = perThread + (i == initThreadCount - 1 ? remainder : 0);
threads.push_back(std::thread(&randomx_init_dataset, randomxDataset, randomxCache, startItem, count));
startItem += count;
}
for (unsigned i = 0; i < threads.size(); ++i) {
threads[i].join();
}
threads.clear();
} else {
rxdebug("%s: initializing dataset with 1 thread\n");
randomx_init_dataset(randomxDataset, randomxCache, 0, datasetItemCount);
}
rxdebug("%s: dataset initialized\n");
myVM = randomx_create_vm(flags, nullptr, randomxDataset);
if(myVM == NULL) {
LogPrintf("RandomXMiner: Cannot create RandomX VM, aborting!\n");
return;
}
//fprintf(stderr,"RandomXMiner: Mining_start=%u\n", Mining_start);
#ifdef ENABLE_WALLET
CBlockTemplate *ptr = CreateNewBlockWithKey(reservekey, pindexPrev->GetHeight()+1, gpucount, 0);
#else
CBlockTemplate *ptr = CreateNewBlockWithKey();
#endif
// fprintf(stderr,"RandomXMiner: created new block with Mining_start=%u\n",Mining_start);
rxdebug("%s: created new block with Mining_start=%u\n",Mining_start);
if ( ptr == 0 )
{
if ( !GetBoolArg("-gen",false))
{
miningTimer.stop();
c.disconnect();
LogPrintf("HushRandomXMiner terminated\n");
return;
}
static uint32_t counter;
if ( counter++ < 10 && ASSETCHAINS_STAKED == 0 )
fprintf(stderr,"RandomXMiner: created illegal blockB, retry with counter=%u\n", counter);
sleep(1);
continue;
}
// fprintf(stderr,"RandomXMiner: getting block template\n");
rxdebug("%s: getting block template\n");
unique_ptr<CBlockTemplate> pblocktemplate(ptr);
if (!pblocktemplate.get())
{
if (GetArg("-mineraddress", "").empty()) {
LogPrintf("Error in HushRandomXMiner: 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 HushRandomXMiner: 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);
}
rxdebug("%s: incrementing extra nonce\n");
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
// fprintf(stderr,"RandomXMiner: %u transactions in block\n",(int32_t)pblock->vtx.size());
LogPrintf("Running HushRandomXMiner 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,"RandomXMiner: gotinvalid=%d\n",gotinvalid);
break;
}
hush_longestchain();
// fprintf(stderr,"RandomXMiner: solving with nNonce = %s\n",pblock->nNonce.ToString().c_str());
rxdebug("%s: solving with nNonce = %s\n",pblock->nNonce.ToString().c_str());
arith_uint256 hashTarget;
hashTarget = HASHTarget;
CDataStream randomxInput(SER_NETWORK, PROTOCOL_VERSION);
// Use the current block as randomx input
randomxInput << pblocktemplate->block;
// std::cerr << "RandomXMiner: randomxInput=" << HexStr(randomxInput) << "\n";
// fprintf(stderr,"RandomXMiner: created randomxKey=%s , randomxInput.size=%lu\n", randomxKey, randomxInput.size() ); //randomxInput);
rxdebug("%s: randomxKey=%s randomxInput=%s\n", randomxKey, HexStr(randomxInput).c_str());
rxdebug("%s: calculating randomx hash\n");
randomx_calculate_hash(myVM, &randomxInput, sizeof randomxInput, randomxHash);
rxdebug("%s: calculated randomx hash\n");
rxdebug("%s: randomxHash=");
if (fRandomXDebug) {
for (unsigned i = 0; i < RANDOMX_HASH_SIZE; ++i) {
printf("%02x", randomxHash[i] & 0xff);
}
printf("\n");
}
// Use randomx 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.
rxdebug("%s: Checking solution against target\n");
pblock->nSolution = soln;
solutionTargetChecks.increment();
2 years ago
// fprintf(stderr,"%s: solutionTargetChecks=%lu\n", __func__, solutionTargetChecks.get());
B = *pblock;
h = UintToArith256(B.GetHash());
2 years ago
rxdebug("%s: h=");
if (fRandomXDebug) {
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");
}
2 years ago
if ( h > hashTarget )
{
rxdebug("%s: h > hashTarget");
return false;
}
CValidationState state;
if ( !TestBlockValidity(state,B, chainActive.LastTip(), true, false))
{
h = UintToArith256(B.GetHash());
2 years ago
fprintf(stderr,"RandomXMiner: Invalid randomx block mined, try again ");
for (z=31; z>=0; z--)
fprintf(stderr,"%02x",((uint8_t *)&h)[z]);
gotinvalid = 1;
2 years ago
fprintf(stderr,"\n");
return(false);
}
SetThreadPriority(THREAD_PRIORITY_NORMAL);
LogPrintf("HushRandomXMiner:\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(RandomXSolverCancelCheck)> cancelled = [&m_cs, &cancelSolver](RandomXSolverCancelCheck pos) {
std::lock_guard<std::mutex> lock{m_cs};
return cancelSolver;
};
2 years ago
try {
2 years ago
// Use the randomX hash as the block solution
std::vector<unsigned char> sol_char(randomxHash, randomxHash+32);
bool found = validBlock(sol_char);
if (found) {
rxdebug("%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 {
rxdebug("%s: solution not found, validBlock=false\n");
}
} catch (RandomXSolverCanceledException&) {
LogPrintf("HushRandomXMiner solver canceled\n");
2 years ago
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 )
{
2 years ago
fprintf(stderr,"%s: no nodes, break\n", __func__);
break;
}
}
if ((UintToArith256(pblock->nNonce) & 0xffff) == 0xffff)
{
2 years ago
fprintf(stderr,"%s: nonce & 0xffff == 0xffff, break\n", __func__);
break;
}
// Update nNonce and nTime
pblock->nNonce = ArithToUint256(UintToArith256(pblock->nNonce) + 1);
pblock->nBits = savebits;
}
rxdebug("%s: going to destroy rx VM\n");
randomx_destroy_vm(myVM);
rxdebug("%s: destroyed VM\n");
}
} catch (const boost::thread_interrupted&) {
miningTimer.stop();
c.disconnect();
randomx_destroy_vm(myVM);
LogPrintf("%s: destroyed vm via thread interrupt\n", __func__);
randomx_release_dataset(randomxDataset);
rxdebug("%s: released dataset via thread interrupt\n");
randomx_release_cache(randomxCache);
rxdebug("%s: released cache via thread interrupt\n");
LogPrintf("HushRandomXMiner terminated\n");
throw;
} catch (const std::runtime_error &e) {
miningTimer.stop();
c.disconnect();
fprintf(stderr,"RandomXMiner: runtime error: %s\n", e.what());
randomx_destroy_vm(myVM);
LogPrintf("%s: destroyed vm because of error\n", __func__);
randomx_release_dataset(randomxDataset);
rxdebug("%s: released dataset because of error\n");
randomx_release_cache(randomxCache);
rxdebug("%s: released cache because of error\n");
return;
}
randomx_release_dataset(randomxDataset);
rxdebug("%s: released dataset in normal exit\n");
randomx_release_cache(randomxCache);
rxdebug("%s: released cache in normal exit\n");
miningTimer.stop();
c.disconnect();
}
#ifdef ENABLE_WALLET
void static BitcoinMiner(CWallet *pwallet)
#else
void static BitcoinMiner()
#endif
{
LogPrintf("HushMiner started\n");
SetThreadPriority(THREAD_PRIORITY_LOWEST);
RenameThread("hush-miner");
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;
unsigned int n = chainparams.EquihashN();
unsigned int k = chainparams.EquihashK();
3 years ago
uint8_t *script; uint64_t total; int32_t i,j,gpucount=HUSH_MAXGPUCOUNT,notaryid = -1;
while ( (ASSETCHAIN_INIT == 0 || HUSH_INITDONE == 0) )
8 years ago
{
sleep(1);
3 years ago
if ( hush_baseid(SMART_CHAIN_SYMBOL) < 0 )
7 years ago
break;
8 years ago
}
if ( notaryid != My_notaryid )
My_notaryid = notaryid;
8 years ago
std::string solver;
5 years ago
if ( ASSETCHAINS_NK[0] == 0 && ASSETCHAINS_NK[1] == 0 )
solver = "tromp";
else
solver = "default";
assert(solver == "tromp" || solver == "default");
LogPrint("pow", "Using Equihash solver \"%s\" with n = %u, k = %u\n", solver, n, k);
if ( SMART_CHAIN_SYMBOL[0] != 0 )
fprintf(stderr,"notaryid.%d Mining.%s with %s\n",notaryid,SMART_CHAIN_SYMBOL,solver.c_str());
std::mutex m_cs;
bool cancelSolver = false;
boost::signals2::connection c = uiInterface.NotifyBlockTip.connect(
6 years ago
[&m_cs, &cancelSolver](const uint256& hashNewTip) mutable {
std::lock_guard<std::mutex> lock{m_cs};
cancelSolver = true;
}
);
miningTimer.start();
try {
if ( SMART_CHAIN_SYMBOL[0] != 0 )
fprintf(stderr,"try %s Mining with %s\n",SMART_CHAIN_SYMBOL,solver.c_str());
8 years ago
while (true)
{
3 years ago
if (chainparams.MiningRequiresPeers()) {
//if ( ASSETCHAINS_SEED != 0 && chainActive.LastTip()->GetHeight() < 100 )
8 years ago
// break;
// Busy-wait for the network to come online so we don't waste time mining
// on an obsolete chain. In regtest mode we expect to fly solo.
miningTimer.stop();
do {
bool fvNodesEmpty;
{
//LOCK(cs_vNodes);
fvNodesEmpty = vNodes.empty();
}
7 years ago
if (!fvNodesEmpty )//&& !IsInitialBlockDownload())
break;
MilliSleep(15000);
//fprintf(stderr,"fvNodesEmpty %d IsInitialBlockDownload(%s) %d\n",(int32_t)fvNodesEmpty,SMART_CHAIN_SYMBOL,(int32_t)IsInitialBlockDownload());
} while (true);
//fprintf(stderr,"%s Found peers\n",SMART_CHAIN_SYMBOL);
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 )
8 years ago
{
Mining_height = pindexPrev->GetHeight()+1;
8 years ago
Mining_start = (uint32_t)time(NULL);
}
if ( SMART_CHAIN_SYMBOL[0] != 0 && ASSETCHAINS_STAKED == 0 )
7 years ago
{
//fprintf(stderr,"%s create new block ht.%d\n",SMART_CHAIN_SYMBOL,Mining_height);
//sleep(3);
7 years ago
}
#ifdef ENABLE_WALLET
// notaries always default to staking
CBlockTemplate *ptr = CreateNewBlockWithKey(reservekey, pindexPrev->GetHeight()+1, gpucount, ASSETCHAINS_STAKED != 0 && HUSH_MININGTHREADS == 0);
#else
7 years ago
CBlockTemplate *ptr = CreateNewBlockWithKey();
#endif
7 years ago
if ( ptr == 0 )
{
if ( !GetBoolArg("-gen",false))
{
miningTimer.stop();
c.disconnect();
LogPrintf("HushMiner terminated\n");
return;
}
static uint32_t counter;
5 years ago
if ( counter++ < 10 && ASSETCHAINS_STAKED == 0 )
5 years ago
fprintf(stderr,"created illegal blockB, retry\n");
sleep(1);
continue;
7 years ago
}
6 years ago
//fprintf(stderr,"get template\n");
7 years ago
unique_ptr<CBlockTemplate> pblocktemplate(ptr);
if (!pblocktemplate.get())
{
if (GetArg("-mineraddress", "").empty()) {
LogPrintf("Error in HushMiner: 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 HushMiner: Invalid -mineraddress\n");
}
return;
}
CBlock *pblock = &pblocktemplate->block;
if ( SMART_CHAIN_SYMBOL[0] != 0 )
{
if ( ASSETCHAINS_REWARD[0] == 0 && !ASSETCHAINS_LASTERA )
{
6 years ago
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);
6 years ago
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);
6 years ago
}
}
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
//fprintf(stderr,"Running HushMiner.%s with %u transactions in block\n",solver.c_str(),(int32_t)pblock->vtx.size());
LogPrintf("Running HushMiner.%s with %u transactions in block (%u bytes)\n",solver.c_str(),pblock->vtx.size(),::GetSerializeSize(*pblock,SER_NETWORK,PROTOCOL_VERSION));
3 years ago
// Search
uint32_t savebits; int64_t nStart = GetTime();
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus());
8 years ago
savebits = pblock->nBits;
HASHTarget = arith_uint256().SetCompact(savebits);
7 years ago
roundrobin_delay = ROUNDROBIN_DELAY;
11 months ago
Mining_start = 0;
5 years ago
//else if ( ASSETCHAINS_ADAPTIVEPOW > 0 )
// HASHTarget_POW = hush_adaptivepow_target(Mining_height,HASHTarget,pblock->nTime);
gotinvalid = 0;
8 years ago
while (true)
{
5 years ago
//fprintf(stderr,"gotinvalid.%d\n",gotinvalid);
5 years ago
if ( gotinvalid != 0 )
break;
hush_longestchain();
// Hash state
3 years ago
HUSH_CHOSEN_ONE = 0;
crypto_generichash_blake2b_state state;
EhInitialiseState(n, k, state);
// I = the block header minus nonce and solution.
CEquihashInput I{*pblock};
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << I;
// H(I||...
crypto_generichash_blake2b_update(&state, (unsigned char*)&ss[0], ss.size());
// H(I||V||...
crypto_generichash_blake2b_state curr_state;
curr_state = state;
8 years ago
crypto_generichash_blake2b_update(&curr_state,pblock->nNonce.begin(),pblock->nNonce.size());
// (x_1, x_2, ...) = A(I, V, n, k)
8 years ago
LogPrint("pow", "Running Equihash solver \"%s\" with nNonce = %s\n",solver, pblock->nNonce.ToString());
arith_uint256 hashTarget;
if ( HUSH_MININGTHREADS > 0 && ASSETCHAINS_STAKED > 0 && ASSETCHAINS_STAKED < 100 && Mining_height > 10 )
hashTarget = HASHTarget_POW;
//else if ( ASSETCHAINS_ADAPTIVEPOW > 0 )
// hashTarget = HASHTarget_POW;
else hashTarget = HASHTarget;
std::function<bool(std::vector<unsigned char>)> validBlock =
#ifdef ENABLE_WALLET
6 years ago
[&pblock, &hashTarget, &pwallet, &reservekey, &m_cs, &cancelSolver, &chainparams]
#else
6 years ago
[&pblock, &hashTarget, &m_cs, &cancelSolver, &chainparams]
#endif
6 years ago
(std::vector<unsigned char> soln) {
6 years ago
int32_t z; arith_uint256 h; CBlock B;
// Write the solution to the hash and compute the result.
LogPrint("pow", "- Checking solution against target\n");
pblock->nSolution = soln;
solutionTargetChecks.increment();
2 years ago
// fprintf(stderr, "%s: solutionTargetChecks=%lu\n", __func__, solutionTargetChecks.get());
6 years ago
B = *pblock;
h = UintToArith256(B.GetHash());
/*for (z=31; z>=16; z--)
6 years ago
fprintf(stderr,"%02x",((uint8_t *)&h)[z]);
6 years ago
fprintf(stderr," mined ");
for (z=31; z>=16; z--)
fprintf(stderr,"%02x",((uint8_t *)&HASHTarget)[z]);
6 years ago
fprintf(stderr," hashTarget ");
for (z=31; z>=16; z--)
fprintf(stderr,"%02x",((uint8_t *)&HASHTarget_POW)[z]);
6 years ago
fprintf(stderr," POW\n");*/
6 years ago
if ( h > hashTarget )
{
//if ( ASSETCHAINS_STAKED != 0 && HUSH_MININGTHREADS == 0 )
// MilliSleep(30);
6 years ago
return false;
}
if ( IS_HUSH_NOTARY != 0 && B.nTime > GetTime() )
8 years ago
{
//fprintf(stderr,"need to wait %d seconds to submit block\n",(int32_t)(B.nTime - GetTime()));
while ( GetTime() < B.nTime-2 )
6 years ago
{
sleep(1);
if ( chainActive.LastTip()->GetHeight() >= Mining_height )
{
fprintf(stderr,"new block arrived\n");
return(false);
}
6 years ago
}
}
6 years ago
if ( ASSETCHAINS_STAKED == 0 )
8 years ago
{
if ( IS_HUSH_NOTARY != 0 )
6 years ago
{
6 years ago
int32_t r;
6 years ago
if ( (r= ((Mining_height + NOTARY_PUBKEY33[16]) % 64) / 8) > 0 )
6 years ago
MilliSleep((rand() % (r * 1000)) + 1000);
}
8 years ago
}
6 years ago
else
8 years ago
{
uint256 tmp = B.GetHash();
int32_t z; for (z=31; z>=0; z--)
fprintf(stderr,"%02x",((uint8_t *)&tmp)[z]);
fprintf(stderr," mined %s block %d!\n",SMART_CHAIN_SYMBOL,Mining_height);
8 years ago
}
CValidationState state;
if ( !TestBlockValidity(state,B, chainActive.LastTip(), true, false))
7 years ago
{
h = UintToArith256(B.GetHash());
5 years ago
//for (z=31; z>=0; z--)
// fprintf(stderr,"%02x",((uint8_t *)&h)[z]);
5 years ago
//fprintf(stderr," Invalid block mined, try again\n");
5 years ago
gotinvalid = 1;
return(false);
7 years ago
}
3 years ago
HUSH_CHOSEN_ONE = 1;
// Found a solution
SetThreadPriority(THREAD_PRIORITY_NORMAL);
LogPrintf("HushMiner:\n");
6 years ago
LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", B.GetHash().GetHex(), HASHTarget.GetHex());
#ifdef ENABLE_WALLET
6 years ago
if (ProcessBlockFound(&B, *pwallet, reservekey)) {
#else
6 years ago
if (ProcessBlockFound(&B)) {
#endif
6 years ago
// Ignore chain updates caused by us
std::lock_guard<std::mutex> lock{m_cs};
cancelSolver = false;
}
3 years ago
HUSH_CHOSEN_ONE = 0;
6 years ago
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
ehSolverRuns.increment();
throw boost::thread_interrupted();
}
return true;
};
std::function<bool(EhSolverCancelCheck)> cancelled = [&m_cs, &cancelSolver](EhSolverCancelCheck pos) {
8 years ago
std::lock_guard<std::mutex> lock{m_cs};
6 years ago
return cancelSolver;
};
// TODO: factor this out into a function with the same API for each solver.
if (solver == "tromp" ) { //&& notaryid >= 0 ) {
// Create solver and initialize it.
equi eq(1);
eq.setstate(&curr_state);
6 years ago
// Initialization done, start algo driver.
eq.digit0(0);
eq.xfull = eq.bfull = eq.hfull = 0;
6 years ago
eq.showbsizes(0);
for (u32 r = 1; r < WK; r++) {
(r&1) ? eq.digitodd(r, 0) : eq.digiteven(r, 0);
eq.xfull = eq.bfull = eq.hfull = 0;
eq.showbsizes(r);
}
6 years ago
eq.digitK(0);
ehSolverRuns.increment();
6 years ago
// Convert solution indices to byte array (decompress) and pass it to validBlock method.
for (size_t s = 0; s < std::min(MAXSOLS, eq.nsols); s++) {
6 years ago
LogPrint("pow", "Checking solution %d\n", s+1);
std::vector<eh_index> index_vector(PROOFSIZE);
for (size_t i = 0; i < PROOFSIZE; i++) {
index_vector[i] = eq.sols[s][i];
}
std::vector<unsigned char> sol_char = GetMinimalFromIndices(index_vector, DIGITBITS);
6 years ago
if (validBlock(sol_char)) {
// 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 {
try {
// If we find a valid block, we rebuild
bool found = EhOptimisedSolve(n, k, curr_state, validBlock, cancelled);
ehSolverRuns.increment();
if (found) {
6 years ago
int32_t i; uint256 hash = pblock->GetHash();
5 years ago
//for (i=0; i<32; i++)
// fprintf(stderr,"%02x",((uint8_t *)&hash)[i]);
//fprintf(stderr," <- %s Block found %d\n",SMART_CHAIN_SYMBOL,Mining_height);
5 years ago
//FOUND_BLOCK = 1;
//HUSH_MAYBEMINED = Mining_height;
6 years ago
break;
}
} catch (EhSolverCancelledException&) {
LogPrint("pow", "Equihash solver cancelled\n");
std::lock_guard<std::mutex> lock{m_cs};
cancelSolver = false;
}
}
6 years ago
// Check for stop or if block needs to be rebuilt
boost::this_thread::interruption_point();
// Regtest mode doesn't require peers
5 years ago
/*if ( FOUND_BLOCK != 0 )
6 years ago
{
FOUND_BLOCK = 0;
fprintf(stderr,"FOUND_BLOCK!\n");
//sleep(2000);
5 years ago
} */
6 years ago
if (vNodes.empty() && chainparams.MiningRequiresPeers())
{
if ( SMART_CHAIN_SYMBOL[0] == 0 || Mining_height > ASSETCHAINS_MINHEIGHT )
6 years ago
{
fprintf(stderr,"no nodes, break\n");
break;
}
}
6 years ago
if ((UintToArith256(pblock->nNonce) & 0xffff) == 0xffff)
8 years ago
{
//if ( 0 && SMART_CHAIN_SYMBOL[0] != 0 )
6 years ago
fprintf(stderr,"0xffff, break\n");
8 years ago
break;
8 years ago
}
6 years ago
if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)
{
if ( 0 && SMART_CHAIN_SYMBOL[0] != 0 )
6 years ago
fprintf(stderr,"timeout, break\n");
break;
}
if ( pindexPrev != chainActive.LastTip() )
6 years ago
{
if ( 0 && SMART_CHAIN_SYMBOL[0] != 0 )
6 years ago
fprintf(stderr,"Tip advanced, break\n");
break;
}
// Update nNonce and nTime
pblock->nNonce = ArithToUint256(UintToArith256(pblock->nNonce) + 1);
pblock->nBits = savebits;
if ( ASSETCHAINS_ADAPTIVEPOW > 0 )
{
UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
HASHTarget.SetCompact(pblock->nBits);
5 years ago
hashTarget = HASHTarget;
5 years ago
savebits = pblock->nBits;
//hashTarget = HASHTarget_POW = hush_adaptivepow_target(Mining_height,HASHTarget,pblock->nTime);
}
6 years ago
/*if ( NOTARY_PUBKEY33[0] == 0 )
6 years ago
{
6 years ago
int32_t percPoS;
6 years ago
UpdateTime(pblock, consensusParams, pindexPrev);
if (consensusParams.fPowAllowMinDifficultyBlocks)
6 years ago
{
// Changing pblock->nTime can change work required on testnet:
HASHTarget.SetCompact(pblock->nBits);
HASHTarget_POW = hush_PoWtarget(&percPoS,HASHTarget,Mining_height,ASSETCHAINS_STAKED);
6 years ago
}
6 years ago
}*/
}
}
}
6 years ago
catch (const boost::thread_interrupted&)
{
miningTimer.stop();
c.disconnect();
LogPrintf("HushMiner terminated\n");
6 years ago
throw;
}
catch (const std::runtime_error &e)
{
miningTimer.stop();
c.disconnect();
LogPrintf("HushMiner runtime error: %s\n", e.what());
6 years ago
return;
}
miningTimer.stop();
c.disconnect();
}
#ifdef ENABLE_WALLET
6 years ago
void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads)
#else
6 years ago
void GenerateBitcoins(bool fGenerate, int nThreads)
#endif
{
6 years ago
static boost::thread_group* minerThreads = NULL;
6 years ago
if (nThreads < 0)
nThreads = GetNumCores();
6 years ago
if (minerThreads != NULL)
{
minerThreads->interrupt_all();
delete minerThreads;
minerThreads = NULL;
}
3 years ago
if(fDebug)
fprintf(stderr,"%s: nThreads.%d fGenerate.%d\n",__FUNCTION__, (int32_t)nThreads,fGenerate);
if (nThreads == 0)
return;
if (!fGenerate)
return;
if (pwallet == NULL)
6 years ago
return;
6 years ago
minerThreads = new boost::thread_group();
6 years ago
for (int i = 0; i < nThreads; i++) {
#ifdef ENABLE_WALLET
if ( ASSETCHAINS_ALGO == ASSETCHAINS_EQUIHASH ) {
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_EQUIHASH ) {
minerThreads->create_thread(&BitcoinMiner);
} else if (ASSETCHAINS_ALGO == ASSETCHAINS_RANDOMX) {
minerThreads->create_thread(&RandomXMiner);
}
#endif
6 years ago
}
}
#endif // ENABLE_MINING