diff --git a/contrib/seeds/generate-seeds.py b/contrib/seeds/generate-seeds.py --- a/contrib/seeds/generate-seeds.py +++ b/contrib/seeds/generate-seeds.py @@ -10,6 +10,7 @@ nodes_main.txt nodes_test.txt + nodes_ergon.txt These files must consist of lines in the format @@ -140,6 +141,8 @@ g.write('\n') with open(os.path.join(indir, 'nodes_test.txt'), 'r', encoding="utf8") as f: process_nodes(g, f, 'pnSeed6_test', 18333) + with open(os.path.join(indir, 'nodes_ergon.txt'), 'r', encoding="utf8") as f: + process_nodes(g, f, 'pnSeed6_ergon', 2137) g.write('#endif // BITCOIN_CHAINPARAMSSEEDS_H\n') diff --git a/contrib/seeds/nodes_ergon.txt b/contrib/seeds/nodes_ergon.txt new file mode 100644 --- /dev/null +++ b/contrib/seeds/nodes_ergon.txt @@ -0,0 +1,3 @@ +72.230.176.60:2137 +194.36.145.176:2137 +199.192.16.55:2137 \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -667,6 +667,7 @@ pow/aserti32d.cpp pow/daa.cpp pow/eda.cpp + pow/ergon.cpp pow/grasberg.cpp pow/pow.cpp rest.cpp @@ -878,6 +879,7 @@ pow/aserti32d.cpp pow/daa.cpp pow/eda.cpp + pow/ergon.cpp pow/pow.cpp primitives/block.cpp primitives/transaction.cpp diff --git a/src/bench/duplicate_inputs.cpp b/src/bench/duplicate_inputs.cpp --- a/src/bench/duplicate_inputs.cpp +++ b/src/bench/duplicate_inputs.cpp @@ -41,7 +41,8 @@ coinbaseTx.vin[0].prevout = COutPoint(); coinbaseTx.vout.resize(1); coinbaseTx.vout[0].scriptPubKey = SCRIPT_PUB; - coinbaseTx.vout[0].nValue = GetBlockSubsidy(nHeight, consensusParams); + coinbaseTx.vout[0].nValue = + GetBlockSubsidy(nullptr, 0, nHeight, consensusParams); coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0; naughtyTx.vout.resize(1); diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -58,6 +58,7 @@ CreateBaseChainParams(CBaseChainParams::TESTNET); const auto regtestBaseParams = CreateBaseChainParams(CBaseChainParams::REGTEST); + const auto ergonBaseParams = CreateBaseChainParams(CBaseChainParams::ERGON); SetupCurrencyUnitOptions(argsman); argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, @@ -111,14 +112,14 @@ "Location of the auth cookie. Relative paths will be prefixed " "by a net-specific datadir location. (default: data dir)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - argsman.AddArg("-rpcport=", - strprintf("Connect to JSON-RPC on (default: %u, " - "testnet: %u, regtest: %u)", - defaultBaseParams->RPCPort(), - testnetBaseParams->RPCPort(), - regtestBaseParams->RPCPort()), - ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, - OptionsCategory::OPTIONS); + argsman.AddArg( + "-rpcport=", + strprintf("Connect to JSON-RPC on (default: %u, " + "testnet: %u, regtest: %u, ergon: %u)", + defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), + regtestBaseParams->RPCPort(), ergonBaseParams->RPCPort()), + ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, + OptionsCategory::OPTIONS); argsman.AddArg("-rpcwait", "Wait for RPC server to start", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-rpcuser=", "Username for JSON-RPC connections", @@ -466,6 +467,9 @@ if (gArgs.GetChainName() == CBaseChainParams::REGTEST) { return " regtest"; } + if (gArgs.GetChainName() == CBaseChainParams::ERGON) { + return " ergon"; + } return ""; } std::string PingTimeToString(double seconds) const { diff --git a/src/chainparams.cpp b/src/chainparams.cpp --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -106,6 +106,9 @@ // The staking rewards are enabled by default on mainnet. consensus.enableStakingRewards = true; + // Proportional rewards are disabled on eCash + consensus.enableProportionalReward = false; + // The best chain should have at least this much work. consensus.nMinimumChainWork = ChainParamsConstants::MAINNET_MINIMUM_CHAIN_WORK; @@ -142,6 +145,8 @@ // May 15, 2024 12:00:00 UTC protocol upgrade consensus.leeKuanYewActivationTime = 1715774400; + consensus.emaDAAActivationTime = 1200000000; + /** * The message start string is designed to be unlikely to occur in * normal data. The characters are rarely used upper ASCII, not valid as @@ -258,6 +263,9 @@ // The staking rewards are disabled by default on testnet. consensus.enableStakingRewards = false; + // Proportional rewards are disabled on testnet + consensus.enableProportionalReward = false; + // The best chain should have at least this much work. consensus.nMinimumChainWork = ChainParamsConstants::TESTNET_MINIMUM_CHAIN_WORK; @@ -294,6 +302,8 @@ // May 15, 2024 12:00:00 UTC protocol upgrade consensus.leeKuanYewActivationTime = 1715774400; + consensus.emaDAAActivationTime = 1200000000; + diskMagic[0] = 0x0b; diskMagic[1] = 0x11; diskMagic[2] = 0x09; @@ -394,6 +404,9 @@ // The staking rewards are disabled by default on regtest. consensus.enableStakingRewards = false; + // Proportional rewards are disabled on regtest + consensus.enableProportionalReward = false; + // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00"); @@ -428,6 +441,8 @@ // May 15, 2024 12:00:00 UTC protocol upgrade consensus.leeKuanYewActivationTime = 1715774400; + consensus.emaDAAActivationTime = 1200000000; + diskMagic[0] = 0xfa; diskMagic[1] = 0xbf; diskMagic[2] = 0xb5; @@ -489,6 +504,144 @@ } }; +/** + * Ergon network + */ +class CErgonParams : public CChainParams { +public: + CErgonParams() { + strNetworkID = CBaseChainParams::ERGON; + consensus.nSubsidyHalvingInterval = 144; + consensus.BIP16Height = 0; + consensus.BIP34Height = 0; + consensus.BIP34Hash = BlockHash::fromHex( + "000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8"); + consensus.BIP65Height = 0; + consensus.BIP66Height = 0; + consensus.CSVHeight = 0; + consensus.powLimit = uint256S( + "0000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + // two weeks + consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; + consensus.nPowTargetSpacing = 10 * 60; + consensus.fPowAllowMinDifficultyBlocks = false; + consensus.fPowNoRetargeting = false; + consensus.nValueCalibration = 7100000000000; + + // two days + consensus.nDAAHalfLife = 2 * 24 * 60 * 60; + + // Ergon has no miner fund + consensus.enableMinerFund = false; + + // Ergon has no staking rewards + consensus.enableStakingRewards = false; + + // Proportional rewards are enabled on Ergon + consensus.enableProportionalReward = true; + + // The best chain should have at least this much work. + consensus.nMinimumChainWork = uint256S( + "0000000000000000000000000000000000000000000000000000000000000184"); + + // By default assume that the signatures in ancestors of this block are + // valid. + consensus.defaultAssumeValid = + BlockHash::fromHex("0x000000070e37bfee7e84b94f997f38155a85b22172f5c" + "a25fd4eb3d64c5ad7c"); + + // August 1, 2017 hard fork + consensus.uahfHeight = 0; + + // November 13, 2017 hard fork + consensus.daaHeight = 0; + + // November 15, 2018 hard fork + consensus.magneticAnomalyHeight = 0; + + // November 15, 2019 protocol upgrade + consensus.gravitonHeight = 0; + + // May 15, 2020 12:00:00 UTC protocol upgrade + consensus.phononHeight = 0; + + // Nov 15, 2020 12:00:00 UTC protocol upgrade + consensus.axionHeight = 0; + + // May 15, 2023 12:00:00 UTC protocol upgrade + consensus.wellingtonHeight = 0; + + // Nov 15, 2023 12:00:00 UTC protocol upgrade + consensus.cowperthwaiteHeight = 0; + + // May 15, 2024 12:00:00 UTC protocol upgrade + consensus.leeKuanYewActivationTime = 0x7fffffff; + + consensus.emaDAAActivationTime = 1659182400; + + /** + * The message start string is designed to be unlikely to occur in + * normal data. The characters are rarely used upper ASCII, not valid as + * UTF-8, and produce a large 32-bit integer with any alignment. + */ + diskMagic[0] = 194; + diskMagic[1] = 95; + diskMagic[2] = 143; + diskMagic[3] = 196; + netMagic[0] = 135; + netMagic[1] = 215; + netMagic[2] = 51; + netMagic[3] = 46; + nDefaultPort = 2137; + nPruneAfterHeight = 100000; + m_assumed_blockchain_size = 0; + m_assumed_chain_state_size = 0; + + genesis = + CreateGenesisBlock(1607003022, 92586649, 0x1d0fffff, 1, 0 * COIN); + consensus.hashGenesisBlock = genesis.GetHash(); + assert(consensus.hashGenesisBlock == + uint256S("000000070e37bfee7e84b94f997f38155a85b22172f5ca25fd4eb3" + "d64c5ad7c5")); + assert(genesis.hashMerkleRoot == + uint256S("dc6c10ad2a26613ae9b8a156ed9ca15e3e355a994a7e32cd7a4c3d" + "7a478f57d2")); + + // Note that of those which support the service bits prefix, most only + // support a subset of possible options. This is fine at runtime as + // we'll fall back to using them as an addrfetch if they don't support + // the service bits we want, but we should get them updated to support + // all service bits wanted by any release ASAP to avoid it where + // possible. + vSeeds.clear(); + vSeeds.emplace_back("dnsseed.ergon.moe"); + vSeeds.emplace_back("seed.ergon.loping.net"); + + base58Prefixes[PUBKEY_ADDRESS] = std::vector(1, 0); + base58Prefixes[SCRIPT_ADDRESS] = std::vector(1, 5); + base58Prefixes[SECRET_KEY] = std::vector(1, 128); + base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x88, 0xB2, 0x1E}; + base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4}; + cashaddrPrefix = "ergon"; + + vFixedSeeds = std::vector(std::begin(pnSeed6_ergon), + std::end(pnSeed6_ergon)); + + fDefaultConsistencyChecks = false; + fRequireStandard = true; + m_is_test_chain = false; + m_is_mockable_chain = false; + + checkpointData = {.mapCheckpoints = {}}; + + m_assumeutxo_data = MapAssumeutxo{ + // TODO to be specified in a future patch. + }; + + chainTxData = ChainTxData{1620430294, 1713, 0.00167}; + } +}; + static std::unique_ptr globalChainParams; const CChainParams &Params() { @@ -509,6 +662,10 @@ return std::make_unique(); } + if (chain == CBaseChainParams::ERGON) { + return std::make_unique(); + } + throw std::runtime_error( strprintf("%s: Unknown chain %s.", __func__, chain)); } diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h --- a/src/chainparamsbase.h +++ b/src/chainparamsbase.h @@ -21,6 +21,7 @@ static const std::string MAIN; static const std::string TESTNET; static const std::string REGTEST; + static const std::string ERGON; const std::string &DataDir() const { return strDataDir; } uint16_t RPCPort() const { return m_rpc_port; } diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -13,6 +13,7 @@ const std::string CBaseChainParams::MAIN = "main"; const std::string CBaseChainParams::TESTNET = "test"; const std::string CBaseChainParams::REGTEST = "regtest"; +const std::string CBaseChainParams::ERGON = "ergon"; void SetupChainParamsBaseOptions(ArgsManager &argsman) { argsman.AddArg( @@ -29,6 +30,8 @@ OptionsCategory::CHAINPARAMS); argsman.AddArg("-testnet", "Use the test chain. Equivalent to -chain=test.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); + argsman.AddArg("-ergon", "Use the Ergon chain. Equivalent to -chain=ergon.", + ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); } static std::unique_ptr globalChainBaseParams; @@ -61,6 +64,12 @@ /*chronik_port=*/18442); } + if (chain == CBaseChainParams::ERGON) { + return std::make_unique("ergon", /*rpc_port=*/2136, + 2138, + /*chronik_port=*/2135); + } + throw std::runtime_error( strprintf("%s: Unknown chain %s.", __func__, chain)); } diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h --- a/src/chainparamsseeds.h +++ b/src/chainparamsseeds.h @@ -115,4 +115,9 @@ {{0x26,0x07,0x53,0x00,0x02,0x03,0x63,0x9e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, 18333}, {{0x2a,0x02,0xc2,0x07,0x20,0x27,0x15,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01}, 18333} }; +static SeedSpec6 pnSeed6_ergon[] = { + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x48,0xe6,0xb0,0x3c}, 2137}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc2,0x24,0x91,0xb0}, 2137}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc7,0xc0,0x10,0x37}, 2137} +}; #endif // BITCOIN_CHAINPARAMSSEEDS_H diff --git a/src/consensus/activation.h b/src/consensus/activation.h --- a/src/consensus/activation.h +++ b/src/consensus/activation.h @@ -58,4 +58,7 @@ bool IsLeeKuanYewEnabled(const Consensus::Params ¶ms, const CBlockIndex *pindexPrev); +bool IsErgonEMAEnabled(const Consensus::Params ¶ms, + const CBlockIndex *pindexPrev); + #endif // BITCOIN_CONSENSUS_ACTIVATION_H diff --git a/src/consensus/activation.cpp b/src/consensus/activation.cpp --- a/src/consensus/activation.cpp +++ b/src/consensus/activation.cpp @@ -128,3 +128,14 @@ return IsLeeKuanYewEnabled(params, pindexPrev->GetMedianTimePast()); } + +bool IsErgonEMAEnabled(const Consensus::Params ¶ms, + const CBlockIndex *pindexPrev) { + if (pindexPrev == nullptr) { + return false; + } + + return pindexPrev->GetMedianTimePast() >= + gArgs.GetIntArg("-ergonemaactivationtime", + params.emaDAAActivationTime); +} diff --git a/src/consensus/amount.cpp b/src/consensus/amount.cpp --- a/src/consensus/amount.cpp +++ b/src/consensus/amount.cpp @@ -6,6 +6,7 @@ #include +#include #include #include #include @@ -14,8 +15,12 @@ static const Currency BCHA{COIN, SATOSHI, 8, "BCHA"}; static const Currency XEC{100 * SATOSHI, SATOSHI, 2, "XEC"}; +static const Currency XRG{COIN, SATOSHI, 8, "XRG"}; const Currency &Currency::get() { + if (gArgs.GetChainName() == CBaseChainParams::ERGON) { + return XRG; + } return gArgs.GetBoolArg("-ecash", DEFAULT_ECASH) ? XEC : BCHA; } diff --git a/src/consensus/params.h b/src/consensus/params.h --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -63,6 +63,8 @@ int cowperthwaiteHeight; /** Unix time used for MTP activation of 15 May 2024 12:00:00 UTC upgrade */ int leeKuanYewActivationTime; + /** Block height at which the new EMA daa becomes active */ + int emaDAAActivationTime; /** Enable or disable the miner fund by default */ bool enableMinerFund; @@ -70,6 +72,9 @@ /** Enable or disable the staking rewards by default */ bool enableStakingRewards; + /** Whether to enable Ergon-esque block rewards */ + bool enableProportionalReward; + /** Proof of work parameters */ uint256 powLimit; bool fPowAllowMinDifficultyBlocks; @@ -77,6 +82,8 @@ int64_t nDAAHalfLife; int64_t nPowTargetSpacing; int64_t nPowTargetTimespan; + int64_t nValueCalibration; + std::chrono::seconds PowTargetSpacing() const { return std::chrono::seconds{nPowTargetSpacing}; } diff --git a/src/index/coinstatsindex.cpp b/src/index/coinstatsindex.cpp --- a/src/index/coinstatsindex.cpp +++ b/src/index/coinstatsindex.cpp @@ -113,8 +113,8 @@ bool CoinStatsIndex::WriteBlock(const CBlock &block, const CBlockIndex *pindex) { CBlockUndo block_undo; - const Amount block_subsidy{ - GetBlockSubsidy(pindex->nHeight, Params().GetConsensus())}; + const Amount block_subsidy{GetBlockSubsidy( + pindex->pprev, block.nBits, pindex->nHeight, Params().GetConsensus())}; m_total_subsidy += block_subsidy; // Ignore genesis block @@ -438,8 +438,8 @@ CBlockUndo block_undo; std::pair read_out; - const Amount block_subsidy{ - GetBlockSubsidy(pindex->nHeight, Params().GetConsensus())}; + const Amount block_subsidy{GetBlockSubsidy( + pindex->pprev, block.nBits, pindex->nHeight, Params().GetConsensus())}; m_total_subsidy -= block_subsidy; // Ignore genesis block diff --git a/src/init.h b/src/init.h --- a/src/init.h +++ b/src/init.h @@ -52,7 +52,7 @@ * @pre Parameters should be parsed and config file should be read, * AppInitBasicSetup should have been called. */ -bool AppInitParameterInteraction(Config &config, const ArgsManager &args); +bool AppInitParameterInteraction(Config &config, ArgsManager &args); /** * Initialization sanity checks: ecc init, sanity checks, dir lock. * @note This can be done before daemonization. diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -417,11 +417,13 @@ CreateBaseChainParams(CBaseChainParams::TESTNET); const auto regtestBaseParams = CreateBaseChainParams(CBaseChainParams::REGTEST); + const auto ergonBaseParams = CreateBaseChainParams(CBaseChainParams::ERGON); const auto defaultChainParams = CreateChainParams(CBaseChainParams::MAIN); const auto testnetChainParams = CreateChainParams(CBaseChainParams::TESTNET); const auto regtestChainParams = CreateChainParams(CBaseChainParams::REGTEST); + const auto ergonChainParams = CreateChainParams(CBaseChainParams::ERGON); // Hidden Options std::vector hidden_args = { @@ -444,6 +446,7 @@ "-uiplatform", // TODO remove after the May. 2024 upgrade "-leekuanyewactivationtime", + "-ergonemaactivationtime", }; // Set all of the args and their help @@ -637,10 +640,10 @@ "HTTP/Protobuf connections to access the index. Unlike the " "JSON-RPC, it's ok to have this publicly exposed on the internet. " "This option can be specified multiple times (default: %s; default " - "port: %u, testnet: %u, regtest: %u)", + "port: %u, testnet: %u, regtest: %u, ergon: %u)", Join(chronik::DEFAULT_BINDS, ", "), defaultBaseParams->ChronikPort(), testnetBaseParams->ChronikPort(), - regtestBaseParams->ChronikPort()), + regtestBaseParams->ChronikPort(), ergonBaseParams->ChronikPort()), ArgsManager::ALLOW_STRING | ArgsManager::NETWORK_ONLY, OptionsCategory::CHRONIK); argsman.AddArg("-chroniktokenindex", @@ -692,10 +695,12 @@ "0.0.0.0). Use [host]:port notation for IPv6. Append =onion " "to tag any incoming connections to that address and port as " "incoming Tor connections (default: 127.0.0.1:%u=onion, " - "testnet: 127.0.0.1:%u=onion, regtest: 127.0.0.1:%u=onion)", + "testnet: 127.0.0.1:%u=onion, regtest: 127.0.0.1:%u=onion, " + "ergon: 127.0.0.1:%u=onion)", defaultBaseParams->OnionServiceTargetPort(), testnetBaseParams->OnionServiceTargetPort(), - regtestBaseParams->OnionServiceTargetPort()), + regtestBaseParams->OnionServiceTargetPort(), + ergonBaseParams->OnionServiceTargetPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); argsman.AddArg( @@ -1129,8 +1134,10 @@ argsman.AddArg( "-minrelaytxfee=", strprintf("Fees (in %s/kB) smaller than this are rejected for " - "relaying, mining and transaction creation (default: %s)", - ticker, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE_PER_KB)), + "relaying, mining and transaction creation (default: %s, " + "ergon: %s)", + ticker, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE_PER_KB), + FormatMoney(ERGON_DEFAULT_MIN_RELAY_TX_FEE_PER_KB)), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); argsman.AddArg( "-whitelistrelay", @@ -1156,8 +1163,9 @@ argsman.AddArg( "-blockmintxfee=", strprintf("Set lowest fee rate (in %s/kB) for transactions to " - "be included in block creation. (default: %s)", - ticker, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE_PER_KB)), + "be included in block creation. (default: %s, ergon: %s)", + ticker, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE_PER_KB), + FormatMoney(ERGON_DEFAULT_BLOCK_MIN_TX_FEE_PER_KB)), ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION); argsman.AddArg("-blockversion=", @@ -1218,14 +1226,14 @@ "connects normally using the rpcuser=/rpcpassword= " "pair of arguments. This option can be specified multiple times", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::RPC); - argsman.AddArg("-rpcport=", - strprintf("Listen for JSON-RPC connections on " - "(default: %u, testnet: %u, regtest: %u)", - defaultBaseParams->RPCPort(), - testnetBaseParams->RPCPort(), - regtestBaseParams->RPCPort()), - ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, - OptionsCategory::RPC); + argsman.AddArg( + "-rpcport=", + strprintf("Listen for JSON-RPC connections on (default: %u, " + "testnet: %u, regtest: %u, ergon: %u)", + defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), + regtestBaseParams->RPCPort(), ergonBaseParams->RPCPort()), + ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, + OptionsCategory::RPC); argsman.AddArg( "-rpcallowip=", "Allow JSON-RPC connections from specified source. Valid for " @@ -1678,7 +1686,7 @@ return true; } -bool AppInitParameterInteraction(Config &config, const ArgsManager &args) { +bool AppInitParameterInteraction(Config &config, ArgsManager &args) { const CChainParams &chainparams = config.GetChainParams(); // Step 2: parameter interactions @@ -1906,6 +1914,16 @@ "peertimeout cannot be configured with a negative value.")); } + // Override defaults on Ergon + if (network == CBaseChainParams::ERGON) { + args.SoftSetArg("-blockmintxfee", + FormatMoney(ERGON_DEFAULT_BLOCK_MIN_TX_FEE_PER_KB)); + args.SoftSetArg("-minrelaytxfee", + FormatMoney(ERGON_DEFAULT_MIN_RELAY_TX_FEE_PER_KB)); + args.SoftSetArg("-dustrelayfee", FormatMoney(ERGON_DUST_RELAY_TX_FEE)); + args.SoftSetBoolArg("-avalanche", false); + } + if (args.IsArgSet("-minrelaytxfee")) { Amount n = Amount::zero(); auto parsed = ParseMoney(args.GetArg("-minrelaytxfee", ""), n); diff --git a/src/networks/abc/checkpoints.cpp b/src/networks/abc/checkpoints.cpp --- a/src/networks/abc/checkpoints.cpp +++ b/src/networks/abc/checkpoints.cpp @@ -134,6 +134,8 @@ "36012afca590b1a11466e2206")}, }}; +static CCheckpointData ergonCheckpointData = {.mapCheckpoints = {}}; + const CCheckpointData &CheckpointData(const std::string &chain) { if (chain == CBaseChainParams::MAIN) { return mainNetCheckpointData; @@ -144,6 +146,9 @@ if (chain == CBaseChainParams::REGTEST) { return regTestCheckpointData; } + if (chain == CBaseChainParams::ERGON) { + return ergonCheckpointData; + } throw std::runtime_error( strprintf("%s: Unknown chain %s.", __func__, chain)); diff --git a/src/node/miner.cpp b/src/node/miner.cpp --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -178,14 +178,16 @@ m_last_block_num_txs = nBlockTx; m_last_block_size = nBlockSize; + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainParams); + // Create coinbase transaction. CMutableTransaction coinbaseTx; coinbaseTx.vin.resize(1); coinbaseTx.vin[0].prevout = COutPoint(); coinbaseTx.vout.resize(1); coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn; - coinbaseTx.vout[0].nValue = - nFees + GetBlockSubsidy(nHeight, consensusParams); + coinbaseTx.vout[0].nValue = GetBlockReward(pindexPrev, pblock->nBits, + nHeight, consensusParams, nFees); coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0; const Amount blockReward = coinbaseTx.vout[0].nValue; @@ -229,7 +231,6 @@ // Fill in header. pblock->hashPrevBlock = pindexPrev->GetBlockHash(); UpdateTime(pblock, chainParams, pindexPrev); - pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainParams); pblock->nNonce = 0; pblocktemplate->entries[0].sigChecks = 0; diff --git a/src/policy/policy.h b/src/policy/policy.h --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -27,6 +27,7 @@ * in blocks created by mining code. */ static constexpr Amount DEFAULT_BLOCK_MIN_TX_FEE_PER_KB(1000 * SATOSHI); +static constexpr Amount ERGON_DEFAULT_BLOCK_MIN_TX_FEE_PER_KB(10 * SATOSHI); /** * The maximum size for transactions we're willing to relay/mine. */ @@ -61,9 +62,11 @@ * outputs below the new threshold. */ static constexpr Amount DUST_RELAY_TX_FEE(1000 * SATOSHI); +static constexpr Amount ERGON_DUST_RELAY_TX_FEE(10 * SATOSHI); /** Default for -minrelaytxfee, minimum relay fee for transactions */ static constexpr Amount DEFAULT_MIN_RELAY_TX_FEE_PER_KB(1000 * SATOSHI); +static constexpr Amount ERGON_DEFAULT_MIN_RELAY_TX_FEE_PER_KB(10 * SATOSHI); /** * When transactions fail script evaluations under standard flags, this flagset diff --git a/src/pow/ergon.h b/src/pow/ergon.h new file mode 100644 --- /dev/null +++ b/src/pow/ergon.h @@ -0,0 +1,23 @@ +// Copyright (c) 2023 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_POW_ERGON_H +#define BITCOIN_POW_ERGON_H + +#include + +struct BlockHash; +class CBlockHeader; +class CBlockIndex; +class CChainParams; + +namespace Consensus { +struct Params; +} + +uint32_t GetNextExpWorkRequired(const CBlockIndex *pindex, + const CBlockHeader *pblock, + const Consensus::Params ¶ms); + +#endif // BITCOIN_POW_ERGON_H diff --git a/src/pow/ergon.cpp b/src/pow/ergon.cpp new file mode 100644 --- /dev/null +++ b/src/pow/ergon.cpp @@ -0,0 +1,196 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2016 The Bitcoin Core developers +// Copyright (c) 2017-2020 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * To reduce the impact of timestamp manipulation, we select the block we are + * basing our computation on via a median of 3. + */ +static const CBlockIndex *GetSuitableBlock(const CBlockIndex *pindex) { + assert(pindex->nHeight >= 3); + + /** + * In order to avoid a block with a very skewed timestamp having too much + * influence, we select the median of the 3 top most blocks as a starting + * point. + */ + const CBlockIndex *blocks[3]; + blocks[2] = pindex; + blocks[1] = pindex->pprev; + blocks[0] = blocks[1]->pprev; + + // Sorting network. + if (blocks[0]->nTime > blocks[2]->nTime) { + std::swap(blocks[0], blocks[2]); + } + + if (blocks[0]->nTime > blocks[1]->nTime) { + std::swap(blocks[0], blocks[1]); + } + + if (blocks[1]->nTime > blocks[2]->nTime) { + std::swap(blocks[1], blocks[2]); + } + + // We should have our candidate in the middle now. + return blocks[1]; +} + +static arith_uint256 ComputeEmaTarget(const CBlockIndex *pindexPrev, + const Consensus::Params ¶ms) { + arith_uint256 work = pindexPrev->nChainWork - pindexPrev->pprev->nChainWork; + // arith_uint256 work =GetBlockProof(pindexPrev) + const CBlockIndex *p1 = GetSuitableBlock(pindexPrev); + const CBlockIndex *p0 = GetSuitableBlock(pindexPrev->pprev); + + int64_t t1 = p1->nTime; + int64_t t0 = p0->nTime; + int64_t t = t1 - t0; + + int64_t resistance = 1000; + + /* + Next block difficulty will be calculated with the quadratic + approximation of the formula: + diff' = diff / (1 + (t/T-1) / r) + where t is the recent solve time difference guarded by 3 block median, + T is the target spacing and r is resistance, namely + diff' = diff[ 1 - (t/T-1) / r + (t/T-1)^2 / r^2 ] + The function has a minimum around t_0/T = r/2 - 1 and then starts + growing. For resistance = 1000 t_0 is around 3.5 days. In the event + of a massive hashrate drop, the the difficulty would go up instead + of going down. To prevent that we flatten the parabola + after the minimum. + */ + + int minimum = resistance / 2 - 1; + int normalized_time = t / params.nPowTargetSpacing; + + if (normalized_time > minimum) { + work -= (work * minimum) / resistance - work / resistance - + (work * minimum * minimum) / (resistance * resistance) + + 2 * (work * minimum) / (resistance * resistance) - + work / (resistance * resistance); + } else if (t >= 0) { + work -= (work * t / params.nPowTargetSpacing) / resistance - + work / resistance - + (work * t * t / + (params.nPowTargetSpacing * params.nPowTargetSpacing)) / + (resistance * resistance) + + 2 * (work * t / params.nPowTargetSpacing) / + (resistance * resistance) - + work / (resistance * resistance); + } else { + // Multiplication of arith uint by a negative is unimplemented, for + // negative sign we pull the minus out manually + t = -t; + work += (work * t / params.nPowTargetSpacing) / resistance + + work / resistance + + (work * t * t / + (params.nPowTargetSpacing * params.nPowTargetSpacing)) / + (resistance * resistance) + + 2 * (work * t / params.nPowTargetSpacing) / + (resistance * resistance) + + work / (resistance * resistance); + } + /** + * We need to compute T = (2^256 / W) - 1 but 2^256 doesn't fit in 256 bits. + * By expressing 1 as W / W, we get (2^256 - W) / W, and we can compute + * 2^256 - W as the complement of W. + */ + return (-work) / work; +} + +static arith_uint256 ComputeExpTarget(const CBlockIndex *pindexPrev, + const Consensus::Params ¶ms) { + arith_uint256 work = pindexPrev->nChainWork - pindexPrev->pprev->nChainWork; + // arith_uint256 work =GetBlockProof(pindexPrev) + const CBlockIndex *p1 = GetSuitableBlock(pindexPrev); + const CBlockIndex *p0 = GetSuitableBlock(pindexPrev->pprev); + + int64_t t = p1->nTime - p0->nTime; + + int64_t resistance = 1000; + + /* + Next block difficulty will be calculated with the quadratic + approximation of the formula: + diff' = diff / (1 + (t/T-1) / r) + where t is the recent solve time difference guarded by 3 block median, + T is the target spacing and r is resistance, namely + diff' = diff[ 1 - (t/T-1) / r + (t/T-1)^2 / r^2 ] + The function has a minimum around t_0/T = r/2 - 1 and then starts + growing. For resistance = 1000 t_0 is around 3.5 days. In the event + of a massive hashrate drop, the the difficulty would go up instead + of going down. To prevent that we flatten the parabola + after the minimum. + */ + + int minimum = resistance / 2 - 1; + int normalized_time = t / params.nPowTargetSpacing; + + if (normalized_time > minimum) { + work -= (work * minimum) / resistance - work / resistance - + (work * minimum * minimum) / (resistance * resistance) + + 2 * (work * minimum) / (resistance * resistance) - + work / (resistance * resistance); + } else { + work -= (work * t / params.nPowTargetSpacing) / resistance - + work / resistance - + (work * (t * t) / + (params.nPowTargetSpacing * params.nPowTargetSpacing)) / + (resistance * resistance) + + 2 * (work * t / params.nPowTargetSpacing) / + (resistance * resistance) - + work / (resistance * resistance); + } + + /** + * We need to compute T = (2^256 / W) - 1 but 2^256 doesn't fit in 256 bits. + * By expressing 1 as W / W, we get (2^256 - W) / W, and we can compute + * 2^256 - W as the complement of W. + */ + return (-work) / work; +} + +uint32_t GetNextExpWorkRequired(const CBlockIndex *pindexPrev, + const CBlockHeader *pblock, + const Consensus::Params ¶ms) { + // This cannot handle the genesis block and early blocks in general. + assert(pindexPrev); + if (pindexPrev->nHeight < 4) { + return 0x1a04b500; // should be about right for two s9 + } + arith_uint256 nextTarget = ComputeExpTarget(pindexPrev, params); + if (!IsErgonEMAEnabled(params, pindexPrev)) { + nextTarget = ComputeExpTarget(pindexPrev, params); + } else if (IsErgonEMAEnabled(params, pindexPrev) && + !IsErgonEMAEnabled(params, pindexPrev->pprev)) { + // Due to attack on Jun 24 2022 the difficulty was driven to powLimit, + // we need to jumpstart it back on the fork. + return 0x1a04b500; + // return 0x1b03c53c; //Should result in a non-zero reward. + } else { + nextTarget = ComputeEmaTarget(pindexPrev, params); + } + const arith_uint256 powLimit = UintToArith256(params.powLimit); + if (nextTarget > powLimit) { + return powLimit.GetCompact(); + } + + return nextTarget.GetCompact(); +} diff --git a/src/pow/pow.cpp b/src/pow/pow.cpp --- a/src/pow/pow.cpp +++ b/src/pow/pow.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,10 @@ return pindexPrev->nBits; } + if (params.enableProportionalReward) { + return GetNextExpWorkRequired(pindexPrev, pblock, params); + } + if (IsAxionEnabled(params, pindexPrev)) { return GetNextASERTWorkRequired(pindexPrev, pblock, params); } diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -4,6 +4,7 @@ #include +#include #include #include #include @@ -37,9 +38,20 @@ {"Satoshi (sat)", "Satoshi (sat) (1 / 100" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)"}}, }; +static const unitNameMap xrgUnits = { + {BitcoinUnits::Unit::base, + {"XRG", + "Ergons"}}, + {BitcoinUnits::Unit::sub, + {"Satoshi (sat)", + "Satoshi (sat) (1 / 100" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)"}}, +}; // clang-format on static const unitNameMap &getUnitsAtRuntime() { + if (gArgs.GetChainName() == CBaseChainParams::ERGON) { + return xrgUnits; + } return gArgs.GetBoolArg("-ecash", DEFAULT_ECASH) ? xecUnits : bchUnits; } diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -45,6 +45,7 @@ #define QAPP_APP_NAME_DEFAULT "BitcoinABC-Qt" #define QAPP_APP_NAME_TESTNET "BitcoinABC-Qt-testnet" #define QAPP_APP_NAME_REGTEST "BitcoinABC-Qt-regtest" +#define QAPP_APP_NAME_ERGON "BitcoinABC-Qt-ergon" /* One gigabyte (GB) in bytes */ static constexpr uint64_t GB_BYTES{1'000'000'000}; diff --git a/src/qt/networkstyle.cpp b/src/qt/networkstyle.cpp --- a/src/qt/networkstyle.cpp +++ b/src/qt/networkstyle.cpp @@ -18,7 +18,8 @@ const int iconColorSaturationReduction; } network_styles[] = {{"main", QAPP_APP_NAME_DEFAULT, 0, 0}, {"test", QAPP_APP_NAME_TESTNET, 70, 30}, - {"regtest", QAPP_APP_NAME_REGTEST, 160, 30}}; + {"regtest", QAPP_APP_NAME_REGTEST, 160, 30}, + {"ergon", QAPP_APP_NAME_ERGON, 100, 60}}; // titleAddText needs to be const char* for tr() NetworkStyle::NetworkStyle(const QString &_appName, const int iconColorHueShift, diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -120,9 +120,9 @@ // message()", but "QMessageBox::"! // void PaymentServer::ipcParseCommandLine(int argc, char *argv[]) { - std::array networks = { + std::array networks = { {&CBaseChainParams::MAIN, &CBaseChainParams::TESTNET, - &CBaseChainParams::REGTEST}}; + &CBaseChainParams::REGTEST, &CBaseChainParams::ERGON}}; const std::string *chosenNetwork = nullptr; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -2575,7 +2575,8 @@ ret_all.pushKV("mintxsize", mintxsize == blockMaxSize ? 0 : mintxsize); ret_all.pushKV("outs", outputs); - ret_all.pushKV("subsidy", GetBlockSubsidy(pindex.nHeight, + ret_all.pushKV("subsidy", GetBlockSubsidy(pindex.pprev, block.nBits, + pindex.nHeight, chainman.GetConsensus())); ret_all.pushKV("time", pindex.GetBlockTime()); ret_all.pushKV("total_out", total_out); diff --git a/src/test/activation_tests.cpp b/src/test/activation_tests.cpp --- a/src/test/activation_tests.cpp +++ b/src/test/activation_tests.cpp @@ -85,4 +85,30 @@ BOOST_CHECK(IsLeeKuanYewEnabled(params, activation + 1)); } +BOOST_AUTO_TEST_CASE(isemaenabled) { + CBlockIndex prev; + + const Consensus::Params ¶ms = Params().GetConsensus(); + const auto activation = + gArgs.GetIntArg("-ergonemaactivationtime", params.emaDAAActivationTime); + SetMockTime(activation - 1000000); + + BOOST_CHECK(!IsErgonEMAEnabled(params, nullptr)); + + std::array blocks; + for (size_t i = 1; i < blocks.size(); ++i) { + blocks[i].pprev = &blocks[i - 1]; + } + BOOST_CHECK(!IsErgonEMAEnabled(params, &blocks.back())); + + SetMTP(blocks, activation - 1); + BOOST_CHECK(!IsErgonEMAEnabled(params, &blocks.back())); + + SetMTP(blocks, activation); + BOOST_CHECK(IsErgonEMAEnabled(params, &blocks.back())); + + SetMTP(blocks, activation + 1); + BOOST_CHECK(IsErgonEMAEnabled(params, &blocks.back())); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/cashaddrenc_tests.cpp b/src/test/cashaddrenc_tests.cpp --- a/src/test/cashaddrenc_tests.cpp +++ b/src/test/cashaddrenc_tests.cpp @@ -22,7 +22,7 @@ std::vector GetNetworks() { return {CBaseChainParams::MAIN, CBaseChainParams::TESTNET, - CBaseChainParams::REGTEST}; + CBaseChainParams::REGTEST, CBaseChainParams::ERGON}; } std::vector insecure_GetRandomByteArray(FastRandomContext &rand, diff --git a/src/test/policy_block_tests.cpp b/src/test/policy_block_tests.cpp --- a/src/test/policy_block_tests.cpp +++ b/src/test/policy_block_tests.cpp @@ -58,7 +58,7 @@ CBlockIndex &lastBlockIndexRef = blocks.back(); const Amount blockReward = - GetBlockSubsidy(lastBlockIndexRef.nHeight, consensusParams); + GetBlockSubsidy(nullptr, 0, lastBlockIndexRef.nHeight, consensusParams); auto checkMinerFundPolicy = [&](CBlock block, const CBlockIndex &blockIndex, bool expected) { diff --git a/src/test/validation_tests.cpp b/src/test/validation_tests.cpp --- a/src/test/validation_tests.cpp +++ b/src/test/validation_tests.cpp @@ -37,13 +37,14 @@ BOOST_CHECK_EQUAL(nPreviousSubsidy, 2 * nInitialSubsidy); for (int nHalvings = 0; nHalvings < maxHalvings; nHalvings++) { int nHeight = nHalvings * consensusParams.nSubsidyHalvingInterval; - Amount nSubsidy = GetBlockSubsidy(nHeight, consensusParams); + Amount nSubsidy = GetBlockSubsidy(nullptr, 0, nHeight, consensusParams); BOOST_CHECK(nSubsidy <= nInitialSubsidy); BOOST_CHECK_EQUAL(nSubsidy, nPreviousSubsidy / 2); nPreviousSubsidy = nSubsidy; } BOOST_CHECK_EQUAL( - GetBlockSubsidy(maxHalvings * consensusParams.nSubsidyHalvingInterval, + GetBlockSubsidy(nullptr, 0, + maxHalvings * consensusParams.nSubsidyHalvingInterval, consensusParams), Amount::zero()); } @@ -51,6 +52,7 @@ static void TestBlockSubsidyHalvings(int nSubsidyHalvingInterval) { Consensus::Params consensusParams; consensusParams.nSubsidyHalvingInterval = nSubsidyHalvingInterval; + consensusParams.enableProportionalReward = false; TestBlockSubsidyHalvings(consensusParams); } @@ -68,7 +70,8 @@ const auto chainParams = CreateChainParams(CBaseChainParams::MAIN); Amount nSum = Amount::zero(); for (int nHeight = 0; nHeight < 14000000; nHeight += 1000) { - Amount nSubsidy = GetBlockSubsidy(nHeight, chainParams->GetConsensus()); + Amount nSubsidy = + GetBlockSubsidy(nullptr, 0, nHeight, chainParams->GetConsensus()); BOOST_CHECK(nSubsidy <= 50 * COIN); nSum += 1000 * nSubsidy; BOOST_CHECK(MoneyRange(nSum)); diff --git a/src/util/system.cpp b/src/util/system.cpp --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -278,7 +278,7 @@ // Section names to be recognized in the config file. static const std::set available_sections{ CBaseChainParams::REGTEST, CBaseChainParams::TESTNET, - CBaseChainParams::MAIN}; + CBaseChainParams::MAIN, CBaseChainParams::ERGON}; LOCK(cs_args); std::list unrecognized = m_config_sections; @@ -1134,9 +1134,11 @@ const bool fRegTest = get_net("-regtest"); const bool fTestNet = get_net("-testnet"); + const bool fErgon = get_net("-ergon"); const bool is_chain_arg_set = IsArgSet("-chain"); - if (int(is_chain_arg_set) + int(fRegTest) + int(fTestNet) > 1) { + if (int(is_chain_arg_set) + int(fRegTest) + int(fTestNet) + int(fErgon) > + 1) { throw std::runtime_error("Invalid combination of -regtest, -testnet " "and -chain. Can use at most one."); } @@ -1146,6 +1148,9 @@ if (fTestNet) { return CBaseChainParams::TESTNET; } + if (fErgon) { + return CBaseChainParams::ERGON; + } return GetArg("-chain", CBaseChainParams::MAIN); } diff --git a/src/validation.h b/src/validation.h --- a/src/validation.h +++ b/src/validation.h @@ -190,7 +190,11 @@ */ void StopScriptCheckWorkerThreads(); -Amount GetBlockSubsidy(int nHeight, const Consensus::Params &consensusParams); +Amount GetBlockSubsidy(CBlockIndex *pindexPrev, uint32_t nBits, int nHeight, + const Consensus::Params &consensusParams); + +Amount GetBlockReward(CBlockIndex *pindexPrev, uint32_t nBits, int nHeight, + const Consensus::Params &consensusParams, Amount nFees); bool AbortNode(BlockValidationState &state, const std::string &strMessage, const bilingual_str &userMessage = bilingual_str{}); diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1145,7 +1145,67 @@ return result; } -Amount GetBlockSubsidy(int nHeight, const Consensus::Params &consensusParams) { +static Amount GetErgonBlockSubsidy(CBlockIndex *pindexPrev, uint32_t nBits, + int nHeight, + const Consensus::Params &consensusParams) { + // calculate work based on nBits like in GetBlockProof from chain.cpp + arith_uint256 bnTarget; + bool fNegative; + bool fOverflow; + bnTarget.SetCompact(nBits, &fNegative, &fOverflow); + if (fNegative || fOverflow || bnTarget == 0) { + return Amount::zero(); + } + + arith_uint256 aWork = (~bnTarget / (bnTarget + 1)) + 1; + + const CBlockIndex *emaBlock = pindexPrev; + + while (emaBlock->pprev) { + // first, skip backwards testing IsErgonEMAEnabled + // The below code leverages CBlockIndex::pskip to walk back efficiently. + if (IsErgonEMAEnabled(consensusParams, emaBlock->pskip)) { + // skip backward + emaBlock = emaBlock->pskip; + continue; // continue skipping + } + // cannot skip here, walk back by 1 + if (!IsErgonEMAEnabled(consensusParams, emaBlock->pprev)) { + // found it -- highest block where EMA is not enabled is + // emaBlock->pprev, and emaBlock points to the first block for which + // IsAxionEnabled() == true + break; + } + // Walking back by 1 + emaBlock = emaBlock->pprev; + } + int divisions = nHeight / consensusParams.nSubsidyHalvingInterval; + int emaHeight = emaBlock->nHeight / consensusParams.nSubsidyHalvingInterval; + for (int a = 0; a < divisions; a++) { + // to be tunned in few years based on high quality empirical research + if (a < emaHeight) { + aWork *= 99826; + aWork /= 100000; + } else { + aWork *= 99918; + aWork /= 100000; + } + } + + aWork /= consensusParams.nValueCalibration; // 14200000000000 + uint256 uWork = ArithToUint256(aWork); + int64_t iWork = uWork.GetUint64(0); + Amount nSubsidy = iWork * Amount::satoshi(); + return nSubsidy; +} + +Amount GetBlockSubsidy(CBlockIndex *pindexPrev, uint32_t nBits, int nHeight, + const Consensus::Params &consensusParams) { + if (consensusParams.enableProportionalReward) { + return GetErgonBlockSubsidy(pindexPrev, nBits, nHeight, + consensusParams); + } + int halvings = nHeight / consensusParams.nSubsidyHalvingInterval; // Force block reward to zero when right shift is undefined. if (halvings >= 64) { @@ -1158,6 +1218,16 @@ return ((nSubsidy / SATOSHI) >> halvings) * SATOSHI; } +Amount GetBlockReward(CBlockIndex *pindexPrev, uint32_t nBits, int nHeight, + const Consensus::Params &consensusParams, Amount nFees) { + const Amount subsidy = + GetBlockSubsidy(pindexPrev, nBits, nHeight, consensusParams); + if (consensusParams.enableProportionalReward) { + return nFees / 2 + subsidy; + } + return nFees + subsidy; +} + CoinsViews::CoinsViews(std::string ldb_name, size_t cache_size_bytes, bool in_memory, bool should_wipe) : m_dbview(gArgs.GetDataDirNet() / ldb_name, cache_size_bytes, in_memory, @@ -2104,8 +2174,8 @@ nInputs <= 1 ? 0 : MILLI * (nTime3 - nTime2) / (nInputs - 1), nTimeConnect * MICRO, nTimeConnect * MILLI / nBlocksTotal); - const Amount blockReward = - nFees + GetBlockSubsidy(pindex->nHeight, consensusParams); + const Amount blockReward = GetBlockReward( + pindex->pprev, pindex->nBits, pindex->nHeight, consensusParams, nFees); if (block.vtx[0]->GetValueOut() > blockReward) { LogPrintf("ERROR: ConnectBlock(): coinbase pays too much (actual=%d vs " "limit=%d)\n", @@ -2630,8 +2700,8 @@ m_filterParkingPoliciesApplied.insert(blockhash); const Amount blockReward = - blockFees + - GetBlockSubsidy(pindexNew->nHeight, consensusParams); + GetBlockReward(pindexNew->pprev, pindexNew->nBits, + pindexNew->nHeight, consensusParams, blockFees); std::vector> parkingPolicies; parkingPolicies.emplace_back(std::make_unique( diff --git a/test/lint/check-doc.py b/test/lint/check-doc.py --- a/test/lint/check-doc.py +++ b/test/lint/check-doc.py @@ -29,6 +29,7 @@ "-includeconf", "-regtest", "-testnet", + "-ergon", "-zmqpubhashblock", "-zmqpubhashtx", "-zmqpubrawblock", @@ -58,6 +59,7 @@ "-replayprotectionactivationtime", # Remove after May. 2024 upgrade "-leekuanyewactivationtime", + "-ergonemaactivationtime", }