zcashd/src/consensus/params.cpp

250 lines
11 KiB
C++

// Copyright (c) 2019-2023 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#include "params.h"
#include <amount.h>
#include <key_io.h>
#include <script/standard.h>
#include "upgrades.h"
#include "util/system.h"
#include "util/match.h"
namespace Consensus {
std::optional<int> Params::GetActivationHeight(Consensus::UpgradeIndex idx) const {
auto nActivationHeight = vUpgrades[idx].nActivationHeight;
if (nActivationHeight == Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT) {
return std::nullopt;
} else {
return nActivationHeight;
}
}
bool Params::NetworkUpgradeActive(int nHeight, Consensus::UpgradeIndex idx) const {
return NetworkUpgradeState(nHeight, *this, idx) == UPGRADE_ACTIVE;
}
int Params::HeightOfLatestSettledUpgrade() const {
for (auto idxInt = Consensus::MAX_NETWORK_UPGRADES - 1; idxInt > Consensus::BASE_SPROUT; idxInt--) {
if (vUpgrades[idxInt].hashActivationBlock.has_value()) {
return vUpgrades[idxInt].nActivationHeight;
}
}
return 0;
}
bool Params::FeatureRequired(const Consensus::ConsensusFeature feature) const {
return vRequiredFeatures.count(feature) > 0;
}
bool Params::FeatureActive(const int nHeight, const Consensus::ConsensusFeature feature) const {
return Features.FeatureActive(*this, nHeight, feature);
}
bool Params::FutureTimestampSoftForkActive(int nHeight) const {
return nHeight >= nFutureTimestampSoftForkHeight;
}
int Params::Halving(int nHeight) const {
// zip208
// Halving(height) :=
// floor((height - SlowStartShift) / PreBlossomHalvingInterval), if not IsBlossomActivated(height)
// floor((BlossomActivationHeight - SlowStartShift) / PreBlossomHalvingInterval + (height - BlossomActivationHeight) / PostBlossomHalvingInterval), otherwise
if (NetworkUpgradeActive(nHeight, Consensus::UPGRADE_BLOSSOM)) {
int64_t blossomActivationHeight = vUpgrades[Consensus::UPGRADE_BLOSSOM].nActivationHeight;
// Ideally we would say:
// halvings = (blossomActivationHeight - consensusParams.SubsidySlowStartShift()) / consensusParams.nPreBlossomSubsidyHalvingInterval
// + (nHeight - blossomActivationHeight) / consensusParams.nPostBlossomSubsidyHalvingInterval;
// But, (blossomActivationHeight - consensusParams.SubsidySlowStartShift()) / consensusParams.nPreBlossomSubsidyHalvingInterval
// would need to be treated as a rational number in order for this to work.
// Define scaledHalvings := halvings * consensusParams.nPostBlossomSubsidyHalvingInterval;
int64_t scaledHalvings = ((blossomActivationHeight - SubsidySlowStartShift()) * Consensus::BLOSSOM_POW_TARGET_SPACING_RATIO)
+ (nHeight - blossomActivationHeight);
return (int) (scaledHalvings / nPostBlossomSubsidyHalvingInterval);
} else {
return (nHeight - SubsidySlowStartShift()) / nPreBlossomSubsidyHalvingInterval;
}
}
/**
* This method determines the block height of the `halvingIndex`th
* halving, as known at the specified `nHeight` block height.
*
* Previous implementations of this logic were specialized to the
* first halving.
*/
int Params::HalvingHeight(int nHeight, int halvingIndex) const {
assert(nHeight >= 0);
assert(halvingIndex > 0);
// zip208
// HalvingHeight(i) := max({ height ⦂ N | Halving(height) < i }) + 1
//
// Halving(h) returns the halving index at the specified height. It is
// defined as floor(f(h)) where f is a strictly increasing rational
// function, so it's sufficient to solve for f(height) = halvingIndex
// in the rationals and then take ceiling(height).
//
// H := blossom activation height;
// SS := SubsidySlowStartShift();
// R := 1 / (postInterval / preInterval) = BLOSSOM_POW_TARGET_SPACING_RATIO
// (The following calculation depends on BLOSSOM_POW_TARGET_SPACING_RATIO being an integer.)
//
// preBlossom:
// i = (height - SS) / preInterval
// height = (preInterval * i) + SS
//
// postBlossom:
// i = (H - SS) / preInterval + (HalvingHeight(i) - H) / postInterval
// preInterval = postInterval / R
// i = (H - SS) / (postInterval / R) + (HalvingHeight(i) - H) / postInterval
// i = (R * (H - SS) + HalvingHeight(i) - H) / postInterval
// postInterval * i = R * (H - SS) + HalvingHeight(i) - H
// HalvingHeight(i) = postInterval * i - R * (H - SS) + H
if (NetworkUpgradeActive(nHeight, Consensus::UPGRADE_BLOSSOM)) {
int blossomActivationHeight = vUpgrades[Consensus::UPGRADE_BLOSSOM].nActivationHeight;
return
(nPostBlossomSubsidyHalvingInterval * halvingIndex)
- (BLOSSOM_POW_TARGET_SPACING_RATIO * (blossomActivationHeight - SubsidySlowStartShift()))
+ blossomActivationHeight;
} else {
return (nPreBlossomSubsidyHalvingInterval * halvingIndex) + SubsidySlowStartShift();
}
}
int Params::GetLastFoundersRewardBlockHeight(int nHeight) const {
return HalvingHeight(nHeight, 1) - 1;
}
int Params::FundingPeriodIndex(int fundingStreamStartHeight, int nHeight) const {
int firstHalvingHeight = HalvingHeight(fundingStreamStartHeight, 1);
// If the start height of the funding period is not aligned to a multiple of the
// funding period length, the first funding period will be shorter than the
// funding period length.
auto startPeriodOffset = (fundingStreamStartHeight - firstHalvingHeight) % nFundingPeriodLength;
if (startPeriodOffset < 0) startPeriodOffset += nFundingPeriodLength; // C++ '%' is remainder, not modulus!
return (nHeight - fundingStreamStartHeight + startPeriodOffset) / nFundingPeriodLength;
}
std::variant<FundingStream, FundingStreamError> FundingStream::ValidateFundingStream(
const Consensus::Params& params,
const int startHeight,
const int endHeight,
const std::vector<FundingStreamAddress>& addresses
) {
if (!params.NetworkUpgradeActive(startHeight, Consensus::UPGRADE_CANOPY)) {
return FundingStreamError::CANOPY_NOT_ACTIVE;
}
if (endHeight < startHeight) {
return FundingStreamError::ILLEGAL_RANGE;
}
if (params.FundingPeriodIndex(startHeight, endHeight - 1) >= addresses.size()) {
return FundingStreamError::INSUFFICIENT_ADDRESSES;
}
return FundingStream(startHeight, endHeight, addresses);
};
class GetFundingStreamOrThrow {
public:
FundingStream operator()(const FundingStream& fs) const {
return fs;
}
FundingStream operator()(const FundingStreamError& e) const {
switch (e) {
case FundingStreamError::CANOPY_NOT_ACTIVE:
throw std::runtime_error("Canopy network upgrade not active at funding stream start height.");
case FundingStreamError::ILLEGAL_RANGE:
throw std::runtime_error("Illegal start/end height combination for funding stream.");
case FundingStreamError::INSUFFICIENT_ADDRESSES:
throw std::runtime_error("Insufficient payment addresses to fully exhaust funding stream.");
default:
throw std::runtime_error("Unrecognized error validating funding stream.");
};
}
};
FundingStream FundingStream::ParseFundingStream(
const Consensus::Params& params,
const KeyConstants& keyConstants,
const int startHeight,
const int endHeight,
const std::vector<std::string>& strAddresses)
{
KeyIO keyIO(keyConstants);
// Parse the address strings into concrete types.
std::vector<FundingStreamAddress> addresses;
for (const auto& strAddr : strAddresses) {
auto addr = keyIO.DecodePaymentAddress(strAddr);
if (!addr.has_value()) {
throw std::runtime_error("Funding stream address was not a valid " PACKAGE_NAME " address.");
}
std::visit(match {
[&](const CKeyID& keyId) {
addresses.push_back(GetScriptForDestination(keyId));
},
[&](const CScriptID& scriptId) {
addresses.push_back(GetScriptForDestination(scriptId));
},
[&](const libzcash::SaplingPaymentAddress& zaddr) {
addresses.push_back(zaddr);
},
[&](const auto& zaddr) {
throw std::runtime_error("Funding stream address was not a valid transparent P2SH or Sapling address.");
}
}, addr.value());
}
auto validationResult = FundingStream::ValidateFundingStream(params, startHeight, endHeight, addresses);
return std::visit(GetFundingStreamOrThrow(), validationResult);
};
void Params::AddZIP207FundingStream(
const KeyConstants& keyConstants,
FundingStreamIndex idx,
int startHeight,
int endHeight,
const std::vector<std::string>& strAddresses)
{
vFundingStreams[idx] = FundingStream::ParseFundingStream(*this, keyConstants, startHeight, endHeight, strAddresses);
};
FundingStreamAddress FundingStream::RecipientAddress(const Consensus::Params& params, int nHeight) const
{
auto addressIndex = params.FundingPeriodIndex(startHeight, nHeight);
assert(addressIndex >= 0 && addressIndex < addresses.size());
return addresses[addressIndex];
};
int64_t Params::PoWTargetSpacing(int nHeight) const {
// zip208
// PoWTargetSpacing(height) :=
// PreBlossomPoWTargetSpacing, if not IsBlossomActivated(height)
// PostBlossomPoWTargetSpacing, otherwise.
bool blossomActive = NetworkUpgradeActive(nHeight, Consensus::UPGRADE_BLOSSOM);
return blossomActive ? nPostBlossomPowTargetSpacing : nPreBlossomPowTargetSpacing;
}
int64_t Params::AveragingWindowTimespan(int nHeight) const {
return nPowAveragingWindow * PoWTargetSpacing(nHeight);
}
int64_t Params::MinActualTimespan(int nHeight) const {
return (AveragingWindowTimespan(nHeight) * (100 - nPowMaxAdjustUp)) / 100;
}
int64_t Params::MaxActualTimespan(int nHeight) const {
return (AveragingWindowTimespan(nHeight) * (100 + nPowMaxAdjustDown)) / 100;
}
}