Added ac-eras parameter, which enables multiple reward eras by allowing other ac_reward releated parameters to accept comma separated lists of values, one for each era
This commit is contained in:
@@ -54,7 +54,13 @@ char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN];
|
||||
uint16_t ASSETCHAINS_PORT;
|
||||
uint32_t ASSETCHAIN_INIT;
|
||||
uint32_t ASSETCHAINS_MAGIC = 2387029918;
|
||||
uint64_t ASSETCHAINS_ENDSUBSIDY,ASSETCHAINS_REWARD,ASSETCHAINS_HALVING,ASSETCHAINS_DECAY,ASSETCHAINS_COMMISSION,ASSETCHAINS_SUPPLY = 10;
|
||||
uint64_t ASSETCHAINS_SUPPLY = 10;
|
||||
uint64_t ASSETCHAINS_COMMISSION;
|
||||
|
||||
#define _MAX_ERAS 3
|
||||
int MAX_ERAS = _MAX_ERAS;
|
||||
uint32_t ASSETCHAINS_ERAS = 1;
|
||||
uint64_t ASSETCHAINS_ENDSUBSIDY[_MAX_ERAS],ASSETCHAINS_REWARD[_MAX_ERAS],ASSETCHAINS_HALVING[_MAX_ERAS],ASSETCHAINS_DECAY[_MAX_ERAS];
|
||||
|
||||
uint32_t KOMODO_INITDONE;
|
||||
char KMDUSERPASS[4096],BTCUSERPASS[4096]; uint16_t KMD_PORT = 7771,BITCOIND_PORT = 7771;
|
||||
|
||||
@@ -1498,6 +1498,121 @@ char *argv0names[] =
|
||||
(char *)"MNZ", (char *)"MNZ", (char *)"MNZ", (char *)"MNZ", (char *)"BTCH", (char *)"BTCH", (char *)"BTCH", (char *)"BTCH"
|
||||
};
|
||||
|
||||
int64_t komodo_max_money()
|
||||
{
|
||||
uint64_t max_money;
|
||||
|
||||
if ( (baseid = komodo_baseid(ASSETCHAINS_SYMBOL)) >= 0 && baseid < 32 )
|
||||
max_money = komodo_maxallowed(baseid);
|
||||
else
|
||||
{
|
||||
// figure out max_money by adding up supply and future block rewards, if no ac_END, max_money uses arbitrary 10,000,000 block end
|
||||
max_money = (ASSETCHAINS_SUPPLY+1) * SATOSHIDEN + (ASSETCHAINS_MAGIC & 0xffffff);
|
||||
|
||||
// ASSETCHAINS_ERAS is zero based
|
||||
for ( int j = 0; j <= ASSETCHAINS_ERAS && (j == 0 || ASSETCHAINS_ENDSUBSIDY[j - 1] != 0); j++ )
|
||||
{
|
||||
uint64_t reward = ASSETCHAINS_REWARD[j];
|
||||
if ( reward > 0 )
|
||||
{
|
||||
uint64_t lastEnd = j == 0 ? 0 : ASSETCHAINS_ENDSUBSIDY[j - 1];
|
||||
uint64_t curEnd = ASSETCHAINS_ENDSUBSIDY[j] == 0 ? 10000000 : : ASSETCHAINS_ENDSUBSIDY[j];
|
||||
uint64_t period = ASSETCHAINS_HALVING[j];
|
||||
uint64_t decay = ASSETCHAINS_DECAY[j];
|
||||
|
||||
// if exactly SATOSHIDEN, linear decay to zero or next era, same as:
|
||||
// (next_era_reward + (starting reward - next_era_reward) / 2) * num_blocks
|
||||
if ( decay == SATOSHIDEN )
|
||||
{
|
||||
if ( j < ASSETCHAINS_ERAS )
|
||||
{
|
||||
nextReward = ASSETCHAINS_REWARD[j + 1];
|
||||
}
|
||||
else
|
||||
nextReward = 0;
|
||||
|
||||
// it can go either up or down if linear, swap to prevent underflow
|
||||
if ( nextReward > reward )
|
||||
{
|
||||
tmp = reward;
|
||||
reward = nextReward;
|
||||
nextReward = tmp;
|
||||
}
|
||||
max_money += ((nextReward + ((reward - nextReward + 1) >> 1)) * (curEnd - lastEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( int k = 0; k < curEnd; k += period )
|
||||
{
|
||||
max_money += period * reward;
|
||||
// if zero, we do straight halving
|
||||
reward = decay ? (reward * decay) / SATOSHIDEN : reward >> 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return((int64_t)max_money);
|
||||
}
|
||||
|
||||
uint64_t komodo_ac_block_subsidy(int nHeight)
|
||||
{
|
||||
// we have to find our era, start from beginning reward, and determine current subsidy
|
||||
uint64_t numerator, nSubsidy = 0;
|
||||
int32_t numhalvings, curEra = 0;
|
||||
static uint64_t cached_subsidy; static int32_t cached_numhalvings; static int cached_era;
|
||||
|
||||
|
||||
// if we have zero end block with rewards or non-zero end-block, check further
|
||||
if ( (ASSETCHAINS_ENDSUBSIDY[0] == 0 && ASSETCHAINS_REWARD[0] != 0) || ASSETCHAINS_ENDSUBSIDY[0] != 0 )
|
||||
{
|
||||
// if we have an end block in the first era, find our current era
|
||||
if ( ASSETCHAINS_ENDSUBSIDY[0] != 0 )
|
||||
{
|
||||
for ( curEra = 0; curEra <= ASSETCHAINS_ERAS; curEra++ )
|
||||
{
|
||||
if ( ASSETCHAINS_ENDSUBSIDY[curEra] > nHeight || ASSETCHAINS_ENDSUBSIDY[curEra] == 0 )
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( curEra <= ASSETCHAINS_ERAS )
|
||||
{
|
||||
int nStart = curEra ? ASSETCHAINS_ENDSUBSIDY[curEra - 1] : 0;
|
||||
nSubsidy = ASSETCHAINS_REWARD[curEra];
|
||||
if ( nSubsidy )
|
||||
{
|
||||
if ( ASSETCHAINS_HALVING[curEra] != 0 )
|
||||
{
|
||||
if ( (numhalvings = ((nHeight - nStart) / ASSETCHAINS_HALVING[curEra])) > 0 )
|
||||
{
|
||||
if ( ASSETCHAINS_DECAY[curEra] == 0 )
|
||||
nSubsidy >>= numhalvings;
|
||||
else if ( ASSETCHAINS_DECAY[curEra] == 100000000 && ASSETCHAINS_ENDSUBSIDY[curEra] != 0 )
|
||||
{
|
||||
numerator = (ASSETCHAINS_ENDSUBSIDY[curEra] - nHeight);
|
||||
nSubsidy = (nSubsidy * numerator) / (ASSETCHAINS_ENDSUBSIDY[curEra] - nStart);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( cached_subsidy > 0 && cached_era == curEra && cached_numhalvings == numhalvings )
|
||||
nSubsidy = cached_subsidy;
|
||||
else
|
||||
{
|
||||
for (i=0; i < numhalvings && nSubsidy != 0; i++)
|
||||
nSubsidy = (nSubsidy * ASSETCHAINS_DECAY[curEra]) / 100000000;
|
||||
cached_subsidy = nSubsidy;
|
||||
cached_numhalvings = numhalvings;
|
||||
cached_era = curEra;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return(nSubsidy);
|
||||
}
|
||||
|
||||
void komodo_args(char *argv0)
|
||||
{
|
||||
extern int64_t MAX_MONEY;
|
||||
@@ -1533,57 +1648,85 @@ void komodo_args(char *argv0)
|
||||
}
|
||||
if ( name.c_str()[0] != 0 )
|
||||
{
|
||||
ASSETCHAINS_ERAS = GetArg("-ac_eras",1)
|
||||
if ( ASSETCHAINS_ERAS < 1 || ASSETCHAINS_ERAS > MAX_ERAS )
|
||||
{
|
||||
ASSETCHAINS_ERAS = 1;
|
||||
printf("ASSETCHAINS_ERAS, if specified, must be between 1 and %u. ASSETCHAINS_ERAS set to %u\n", MAX_ERAS, ASSETCHAINS_ERAS);
|
||||
}
|
||||
ASSETCHAINS_ERAS -= 1;
|
||||
|
||||
SplitToi64(GetArg("-ac_end",""), &ASSETCHAINS_ENDSUBSIDY, 0);
|
||||
SplitToi64(GetArg("-ac_reward",""), &ASSETCHAINS_REWARD, 0);
|
||||
SplitToi64(GetArg("-ac_halving",""), &ASSETCHAINS_HALVING, 0);
|
||||
SplitToi64(GetArg("-ac_decay",""), &ASSETCHAINS_DECAY, 0);
|
||||
|
||||
for ( int i = 0; i < MAX_ERAS; i++ )
|
||||
{
|
||||
if ( ASSETCHAINS_HALVING[i] != 0 && ASSETCHAINS_HALVING[i] < 1440 )
|
||||
{
|
||||
ASSETCHAINS_HALVING[i] = 1440;
|
||||
printf("ERA%u: ASSETCHAINS_HALVING must be at least 1440 blocks\n", i);
|
||||
}
|
||||
if ( ASSETCHAINS_DECAY[i] == 100000000 && ASSETCHAINS_ENDSUBSIDY == 0 )
|
||||
{
|
||||
ASSETCHAINS_DECAY[i] = 0;
|
||||
printf("ERA%u: ASSETCHAINS_DECAY of 100000000 means linear and that needs ASSETCHAINS_ENDSUBSIDY\n", i);
|
||||
}
|
||||
else if ( ASSETCHAINS_DECAY[i] > 100000000 )
|
||||
{
|
||||
ASSETCHAINS_DECAY[i] = 0;
|
||||
printf("ERA%u: ASSETCHAINS_DECAY cant be more than 100000000\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
ASSETCHAINS_SUPPLY = GetArg("-ac_supply",10);
|
||||
ASSETCHAINS_ENDSUBSIDY = GetArg("-ac_end",0);
|
||||
ASSETCHAINS_REWARD = GetArg("-ac_reward",0);
|
||||
ASSETCHAINS_HALVING = GetArg("-ac_halving",0);
|
||||
ASSETCHAINS_DECAY = GetArg("-ac_decay",0);
|
||||
ASSETCHAINS_COMMISSION = GetArg("-ac_perc",0);
|
||||
ASSETCHAINS_OVERRIDE_PUBKEY = GetArg("-ac_pubkey","");
|
||||
if ( ASSETCHAINS_HALVING != 0 && ASSETCHAINS_HALVING < 1440 )
|
||||
{
|
||||
ASSETCHAINS_HALVING = 1440;
|
||||
printf("ASSETCHAINS_HALVING must be at least 1440 blocks\n");
|
||||
}
|
||||
if ( ASSETCHAINS_DECAY == 100000000 && ASSETCHAINS_ENDSUBSIDY == 0 )
|
||||
{
|
||||
ASSETCHAINS_DECAY = 0;
|
||||
printf("ASSETCHAINS_DECAY of 100000000 means linear and that needs ASSETCHAINS_ENDSUBSIDY\n");
|
||||
}
|
||||
else if ( ASSETCHAINS_DECAY > 100000000 )
|
||||
{
|
||||
ASSETCHAINS_DECAY = 0;
|
||||
printf("ASSETCHAINS_DECAY cant be more than 100000000\n");
|
||||
}
|
||||
|
||||
if ( strlen(ASSETCHAINS_OVERRIDE_PUBKEY.c_str()) == 66 && ASSETCHAINS_COMMISSION > 0 && ASSETCHAINS_COMMISSION <= 100000000 )
|
||||
decode_hex(ASSETCHAINS_OVERRIDE_PUBKEY33,33,(char *)ASSETCHAINS_OVERRIDE_PUBKEY.c_str());
|
||||
else if ( ASSETCHAINS_COMMISSION != 0 )
|
||||
{
|
||||
ASSETCHAINS_COMMISSION = 0;
|
||||
printf("ASSETCHAINS_COMMISSION needs an ASETCHAINS_OVERRIDE_PUBKEY and cant be more than 100000000 (100%%)\n");
|
||||
printf("ASSETCHAINS_COMMISSION needs an ASSETCHAINS_OVERRIDE_PUBKEY and cant be more than 100000000 (100%%)\n");
|
||||
}
|
||||
if ( ASSETCHAINS_ENDSUBSIDY != 0 || ASSETCHAINS_REWARD != 0 || ASSETCHAINS_HALVING != 0 || ASSETCHAINS_DECAY != 0 || ASSETCHAINS_COMMISSION != 0 )
|
||||
|
||||
if ( ASSETCHAINS_ENDSUBSIDY[0] != 0 || ASSETCHAINS_REWARD[0] != 0 || ASSETCHAINS_HALVING[0] != 0 || ASSETCHAINS_DECAY[0] != 0 || ASSETCHAINS_COMMISSION != 0 )
|
||||
{
|
||||
printf("end.%llu reward.%llu halving.%llu decay.%llu perc.%llu\n",(long long)ASSETCHAINS_ENDSUBSIDY,(long long)ASSETCHAINS_REWARD,(long long)ASSETCHAINS_HALVING,(long long)ASSETCHAINS_DECAY,(long long)ASSETCHAINS_COMMISSION);
|
||||
printf("perc.%llu\n",(long long)ASSETCHAINS_COMMISSION);
|
||||
|
||||
extraptr = extrabuf;
|
||||
memcpy(extraptr,ASSETCHAINS_OVERRIDE_PUBKEY33,33), extralen = 33;
|
||||
extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_ENDSUBSIDY),(void *)&ASSETCHAINS_ENDSUBSIDY);
|
||||
extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_REWARD),(void *)&ASSETCHAINS_REWARD);
|
||||
extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_HALVING),(void *)&ASSETCHAINS_HALVING);
|
||||
extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_DECAY),(void *)&ASSETCHAINS_DECAY);
|
||||
|
||||
// if we have one era, this should create the same data structure as it used to, same if we increase _MAX_ERAS
|
||||
for ( int i = 0; i <= ASSETCHAINS_ERAS; i++ )
|
||||
{
|
||||
printf("ERA%u: end.%llu reward.%llu halving.%llu decay.%llu\n", i,
|
||||
(long long)ASSETCHAINS_ENDSUBSIDY[i],
|
||||
(long long)ASSETCHAINS_REWARD[i],
|
||||
(long long)ASSETCHAINS_HALVING[i],
|
||||
(long long)ASSETCHAINS_DECAY[i]);
|
||||
|
||||
// TODO: Verify that we don't overrun extrabuf here, which is a 256 byte buffer
|
||||
extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_ENDSUBSIDY[i]),(void *)&ASSETCHAINS_ENDSUBSIDY[i]);
|
||||
extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_REWARD[i]),(void *)&ASSETCHAINS_REWARD[i]);
|
||||
extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_HALVING[i]),(void *)&ASSETCHAINS_HALVING[i]);
|
||||
extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_DECAY[i]),(void *)&ASSETCHAINS_DECAY[i]);
|
||||
}
|
||||
extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_COMMISSION),(void *)&ASSETCHAINS_COMMISSION);
|
||||
}
|
||||
|
||||
addn = GetArg("-seednode","");
|
||||
if ( strlen(addn.c_str()) > 0 )
|
||||
ASSETCHAINS_SEED = 1;
|
||||
strncpy(ASSETCHAINS_SYMBOL,name.c_str(),sizeof(ASSETCHAINS_SYMBOL)-1);
|
||||
if ( (baseid= komodo_baseid(ASSETCHAINS_SYMBOL)) >= 0 && baseid < 32 )
|
||||
MAX_MONEY = komodo_maxallowed(baseid);
|
||||
else if ( ASSETCHAINS_REWARD == 0 )
|
||||
MAX_MONEY = (ASSETCHAINS_SUPPLY+1) * SATOSHIDEN;
|
||||
else MAX_MONEY = (ASSETCHAINS_SUPPLY+1) * SATOSHIDEN + ASSETCHAINS_REWARD * (ASSETCHAINS_ENDSUBSIDY==0 ? 10000000 : ASSETCHAINS_ENDSUBSIDY);
|
||||
|
||||
MAX_MONEY = komodo_max_money();
|
||||
|
||||
//printf("baseid.%d MAX_MONEY.%s %.8f\n",baseid,ASSETCHAINS_SYMBOL,(double)MAX_MONEY/SATOSHIDEN);
|
||||
ASSETCHAINS_PORT = komodo_port(ASSETCHAINS_SYMBOL,ASSETCHAINS_SUPPLY,&ASSETCHAINS_MAGIC,extraptr,extralen);
|
||||
|
||||
while ( (dirname= (char *)GetDataDir(false).string().c_str()) == 0 || dirname[0] == 0 )
|
||||
{
|
||||
fprintf(stderr,"waiting for datadir\n");
|
||||
|
||||
46
src/main.cpp
46
src/main.cpp
@@ -1636,11 +1636,10 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex)
|
||||
//uint64_t komodo_moneysupply(int32_t height);
|
||||
extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN];
|
||||
extern uint32_t ASSETCHAINS_MAGIC;
|
||||
extern uint64_t ASSETCHAINS_ENDSUBSIDY,ASSETCHAINS_REWARD,ASSETCHAINS_HALVING,ASSETCHAINS_LINEAR,ASSETCHAINS_COMMISSION,ASSETCHAINS_SUPPLY;
|
||||
extern uint64_t ASSETCHAINS_LINEAR,ASSETCHAINS_COMMISSION,ASSETCHAINS_SUPPLY;
|
||||
|
||||
CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)
|
||||
{
|
||||
static uint64_t cached_subsidy; static int32_t cached_numhalvings;
|
||||
int32_t numhalvings,i; uint64_t numerator; CAmount nSubsidy = 3 * COIN;
|
||||
if ( ASSETCHAINS_SYMBOL[0] == 0 )
|
||||
{
|
||||
@@ -1654,45 +1653,8 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)
|
||||
{
|
||||
if ( nHeight == 1 )
|
||||
return(ASSETCHAINS_SUPPLY * COIN + (ASSETCHAINS_MAGIC & 0xffffff));
|
||||
else if ( ASSETCHAINS_ENDSUBSIDY == 0 || nHeight < ASSETCHAINS_ENDSUBSIDY )
|
||||
{
|
||||
if ( ASSETCHAINS_REWARD == 0 )
|
||||
return(10000);
|
||||
else if ( ASSETCHAINS_ENDSUBSIDY != 0 && nHeight >= ASSETCHAINS_ENDSUBSIDY )
|
||||
return(0);
|
||||
else
|
||||
{
|
||||
nSubsidy = ASSETCHAINS_REWARD;
|
||||
if ( ASSETCHAINS_HALVING != 0 )
|
||||
{
|
||||
if ( (numhalvings= (nHeight / ASSETCHAINS_HALVING)) > 0 )
|
||||
{
|
||||
if ( numhalvings >= 64 && ASSETCHAINS_DECAY == 0 )
|
||||
return(0);
|
||||
if ( ASSETCHAINS_DECAY == 0 )
|
||||
nSubsidy >>= numhalvings;
|
||||
else if ( ASSETCHAINS_DECAY == 100000000 && ASSETCHAINS_ENDSUBSIDY != 0 )
|
||||
{
|
||||
numerator = (ASSETCHAINS_ENDSUBSIDY - nHeight);
|
||||
nSubsidy = (nSubsidy * numerator) / ASSETCHAINS_ENDSUBSIDY;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( cached_subsidy > 0 && cached_numhalvings == numhalvings )
|
||||
nSubsidy = cached_subsidy;
|
||||
else
|
||||
{
|
||||
for (i=0; i<numhalvings&&nSubsidy!=0; i++)
|
||||
nSubsidy = (nSubsidy * ASSETCHAINS_DECAY) / 100000000;
|
||||
cached_subsidy = nSubsidy;
|
||||
cached_numhalvings = numhalvings;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return(nSubsidy);
|
||||
} else return(0);
|
||||
else
|
||||
return(komodo_ac_block_subsidy(nHeight));
|
||||
}
|
||||
/*
|
||||
// Mining slow start
|
||||
@@ -1717,7 +1679,7 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)
|
||||
|
||||
// Subsidy is cut in half every 840,000 blocks which will occur approximately every 4 years.
|
||||
//nSubsidy >>= halvings;
|
||||
return nSubsidy;
|
||||
//return nSubsidy;
|
||||
}
|
||||
|
||||
bool IsInitialBlockDownload()
|
||||
|
||||
34
src/util.cpp
34
src/util.cpp
@@ -18,6 +18,8 @@
|
||||
#include "komodo_defs.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#if (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__))
|
||||
#include <pthread.h>
|
||||
@@ -362,6 +364,38 @@ void ParseParameters(int argc, const char* const argv[])
|
||||
}
|
||||
}
|
||||
|
||||
void SplitToi64(const std::string& strVal, int64_t[_MAX_ERAS] *outVals, const int64_t nDefault)
|
||||
{
|
||||
istringstream ss(strVal);
|
||||
vector<int64_t> vec;
|
||||
|
||||
int64_t i, numVals = 0;
|
||||
|
||||
while ( ss.peek() == ' ' )
|
||||
ss.ignore();
|
||||
|
||||
while ( ss >> i )
|
||||
{
|
||||
outVals[numVals] = i;
|
||||
numVals += 1;
|
||||
|
||||
while ( ss.peek() == ' ' )
|
||||
ss.ignore();
|
||||
if ( ss.peek() == ',' )
|
||||
ss.ignore();
|
||||
while ( ss.peek() == ' ' )
|
||||
ss.ignore();
|
||||
}
|
||||
|
||||
if ( numVals > 0 )
|
||||
nDefault = outVals[numVals - 1];
|
||||
|
||||
for ( i=numVals; i < MAX_ERAS; i++ )
|
||||
{
|
||||
outVals[i] = nDefault;
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetArg(const std::string& strArg, const std::string& strDefault)
|
||||
{
|
||||
if (mapArgs.count(strArg))
|
||||
|
||||
11
src/util.h
11
src/util.h
@@ -157,6 +157,17 @@ inline bool IsSwitchChar(char c)
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Return string argument or default value
|
||||
*
|
||||
* @param strVal string to split
|
||||
* @param outVals array of numbers from string or default
|
||||
* if the string is null, nDefault is used for all array entries
|
||||
* else if the string has fewer than _MAX_ERAS entries, then the last
|
||||
* entry fills remaining entries
|
||||
*/
|
||||
void SplitToi64(const std::string& strVal, int64_t[_MAX_ERAS]& outVals, int64_t nDefault);
|
||||
|
||||
/**
|
||||
* Return string argument or default value
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user