Add founders reward to ChainParams.
Fix bug where subsidy slow shift was ignored.
This commit is contained in:
parent
d5dce9342b
commit
db0f931570
|
@ -5,6 +5,7 @@ bin_PROGRAMS += zcash-gtest
|
|||
zcash_gtest_SOURCES = \
|
||||
gtest/main.cpp \
|
||||
gtest/json_test_vectors.cpp \
|
||||
gtest/test_foundersreward.cpp \
|
||||
gtest/test_jsonspirit.cpp \
|
||||
gtest/test_tautology.cpp \
|
||||
gtest/test_checktransaction.cpp \
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
#include <boost/assign/list_of.hpp>
|
||||
|
||||
#include "base58.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include "chainparamsseeds.h"
|
||||
|
@ -138,6 +140,23 @@ public:
|
|||
// (the tx=... number in the SetBestChain debug.log lines)
|
||||
0 // * estimated number of transactions per day after checkpoint
|
||||
};
|
||||
|
||||
// Founders reward script expects a vector of 2-of-3 multisig addresses
|
||||
vFoundersRewardAddress = {
|
||||
"ADDRESS1", "ADDRESS2", "ADDRESS3", "ADDRESS4",
|
||||
"ADDRESS5", "ADDRESS6", "ADDRESS7", "ADDRESS8",
|
||||
"ADDRESS9", "ADDRESS10", "ADDRESS11", "ADDRESS12",
|
||||
"ADDRESS13", "ADDRESS14", "ADDRESS15", "ADDRESS16",
|
||||
"ADDRESS17", "ADDRESS18", "ADDRESS19", "ADDRESS20",
|
||||
"ADDRESS21", "ADDRESS22", "ADDRESS23", "ADDRESS24",
|
||||
"ADDRESS25", "ADDRESS26", "ADDRESS27", "ADDRESS28",
|
||||
"ADDRESS29", "ADDRESS30", "ADDRESS31", "ADDRESS32",
|
||||
"ADDRESS33", "ADDRESS34", "ADDRESS35", "ADDRESS36",
|
||||
"ADDRESS37", "ADDRESS38", "ADDRESS39", "ADDRESS40",
|
||||
"ADDRESS41", "ADDRESS42", "ADDRESS43", "ADDRESS44",
|
||||
"ADDRESS45", "ADDRESS46", "ADDRESS47", "ADDRESS48",
|
||||
};
|
||||
assert(vFoundersRewardAddress.size() < consensus.nSubsidyHalvingInterval + consensus.SubsidySlowStartShift());
|
||||
}
|
||||
};
|
||||
static CMainParams mainParams;
|
||||
|
@ -202,6 +221,22 @@ public:
|
|||
0
|
||||
};
|
||||
|
||||
// Founders reward script expects a vector of 2-of-3 multisig addresses
|
||||
vFoundersRewardAddress = {
|
||||
"2N2e2FRfP9D1dRN1oRWkH7pbFM69eGNAuQ4", "2NBW8WsA2jUussoJbRv82UXH1BYopkjYqcd", "2N1MudZmwDFTcYiLCZfrcsnhHwaSTTigbcN", "2MxfUJXWKz9D8X3mcMpVcdEJKdJ6zFukca9",
|
||||
"2N8iUwMCpU16VYpKQ1HRM6xfut5FZwGwieM", "2N9hyafTvJVrykBvZDw79j1brozwZNySwPP", "2NFx7tRozsp3kT1M4w4tL9FfnEj8RovzbzN", "2NAqoH96V1RtmK72LEZpJNX1uxhJ5yejRiK",
|
||||
"2MyV7hoV28KS8Uam2Z8nzY3xeo7R3T3TLUr", "2N8Tn19hMoCD4EmCwpg1V8qupVkQLVVPhav", "2NA5UeJU9zAQkSMyy3xpDcjfp4CEyKfzXKp", "2NBERNyXy46CfM9yewGeof4yzC3vkwYnhgS",
|
||||
"2N7fnpAswHb4mnPm2ZjWX3eKkF8hABAYBtQ", "2N9MXGsz7uYaY5ciax6tSMDG7sjZUoLhJTC", "2N5PwzPQFFmLut2XWGQWAmpwKsF8VzUoPtr", "2MvZdDpNP8hWyEqg6zKW9B62YTJqcUwjHr5",
|
||||
"2Mx4KfKJ37EDc3A43Frzof1iEjSe91JUX7d", "2NBMSdXjZ7YqREmwxEtgGryY59KBpqMSs1d", "2N9RbfE4ZCJ3Nx68vPfmvH2M6Q3qicJhagb", "2N4xwfFkFj4DR4NWNbynzP2aJmVcEFnA2DB",
|
||||
"2Mx4TyAwedmsRuDkvMNYGqrcCZfQTfCvxAp", "2Mx4HSVsxEqXjLxn8igJzmCrFdG9XhnNvtf", "2MtLM4SP7LJbBZ5rA5ZG8kAVz9UNrNKuoFB", "2N7SPq83Cbmwuwv5rjNBzVd9QtJKAxxKj8M",
|
||||
"2MwYkbE4U4p9XBsCrupDDkdcDH9L9xvc9Bn", "2MyaeCHpVmckokUi67YP1QK9L3Dkx3Pt86F", "2N7URNgBPXGjqnuPHiynCa6qMMhKm6YEaHr", "2N2eNwGVwj4WwbEdJg7YZDgrnYvDv1ZSNbB",
|
||||
"2MuWAG6BqLM1mtZc67Fv1aKgGwkNQ2akDGt", "2N7XH82MbGwpzbc7PM2aK5CU14bSJvK7Etz", "2MuPX8Ke5TvDDQ1nkqpaPMgYWPyWbFp18Jn", "2NFBST7oK9yw9PaXaq5QhdyYwp5HpHz9m81",
|
||||
"2MuSeMBUrttbjvDZAeQjTrrDeoP197qj2kG", "2N6JU8JNGGAUFknTCuLSuDEEhZJqMfFsH88", "2N4P2MrwtwbiHymQm1RASoVoiH3sFrBpmXa", "2MyhFiVXvVVxUNc8Qh9ppV7jG4NsKpnxige",
|
||||
"2N5dLXUho2GtjuHMWuqixLrHLCwUMcYxd7s", "2N9NhfSiYBt3fhETFR6mQc3uxreEy7simSg", "2NBEEWPY3v38uuC7n1tMtviEY7ND2XzfgSG", "2NCWWj6oREJiMmfJ2bV5sbm1xchMwQfAZ5r",
|
||||
"2N4ACsVCKMvJmtEb3Pd3xkqhJ3rLT4mYx1r", "2MtmMdabcwRJmenswaYtWA675df854KhUxD", "2N2h27Dd87eiGcm7ajvu4hJpXjTm9GkzvLZ", "2NGE19agRXU1EAK3PCLZWXERkpqyUexhk9r",
|
||||
"2N63112wMnBsXTaBFjbCTjW9LuyTXQmvEdw", "2NBkHxgkYZbU56zsoLNsP5WZVfMtBK6X8WK", "2N5pK7NfKo6d9qBmsKggpwuvQeMxGf65SLH", "2N5jHzgCg9a9uAcLaT2jij8WKTZzWbVNC5c",
|
||||
};
|
||||
assert(vFoundersRewardAddress.size() < consensus.nSubsidyHalvingInterval + consensus.SubsidySlowStartShift());
|
||||
}
|
||||
};
|
||||
static CTestNetParams testNetParams;
|
||||
|
@ -259,6 +294,10 @@ public:
|
|||
0,
|
||||
0
|
||||
};
|
||||
|
||||
// Founders reward script expects a vector of 2-of-3 multisig addresses
|
||||
vFoundersRewardAddress = { "2N2e2FRfP9D1dRN1oRWkH7pbFM69eGNAuQ4" };
|
||||
assert(vFoundersRewardAddress.size() < consensus.nSubsidyHalvingInterval + consensus.SubsidySlowStartShift());
|
||||
}
|
||||
};
|
||||
static CRegTestParams regTestParams;
|
||||
|
@ -298,3 +337,29 @@ bool SelectParamsFromCommandLine()
|
|||
SelectParams(network);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Block height must be >0 and <=last founders reward block height
|
||||
// Index variable i ranges from 0 - (vFoundersRewardAddress.size()-1)
|
||||
std::string CChainParams::GetFoundersRewardAddress(int nHeight) const
|
||||
{
|
||||
int maxHeight = consensus.GetLastFoundersRewardBlockHeight();
|
||||
assert(nHeight>0 && nHeight<=maxHeight);
|
||||
|
||||
size_t i = (size_t)floor((double(nHeight-1)/maxHeight)*vFoundersRewardAddress.size());
|
||||
return vFoundersRewardAddress[i];
|
||||
}
|
||||
|
||||
// Block height must be >0 and <=last founders reward block height
|
||||
// The founders reward address is expected to be a multisig (P2SH) address
|
||||
CScript CChainParams::GetFoundersRewardScript(int nHeight) const
|
||||
{
|
||||
assert(nHeight > 0 && nHeight <= consensus.GetLastFoundersRewardBlockHeight());
|
||||
|
||||
CBitcoinAddress address(GetFoundersRewardAddress(nHeight).c_str());
|
||||
assert(address.IsValid());
|
||||
assert(address.IsScript());
|
||||
CScriptID scriptID = get<CScriptID>(address.Get()); // Get() returns a boost variant
|
||||
CScript script = CScript() << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL;
|
||||
return script;
|
||||
}
|
||||
|
|
|
@ -77,6 +77,9 @@ public:
|
|||
const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; }
|
||||
const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
|
||||
const Checkpoints::CCheckpointData& Checkpoints() const { return checkpointData; }
|
||||
/** Return the founder's reward address and script for a given block height */
|
||||
std::string GetFoundersRewardAddress(int height) const;
|
||||
CScript GetFoundersRewardScript(int height) const;
|
||||
protected:
|
||||
CChainParams() {}
|
||||
|
||||
|
@ -102,6 +105,7 @@ protected:
|
|||
bool fMineBlocksOnDemand;
|
||||
bool fTestnetToBeDeprecatedFieldRPC;
|
||||
Checkpoints::CCheckpointData checkpointData;
|
||||
std::vector<std::string> vFoundersRewardAddress;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -32,6 +32,9 @@ struct Params {
|
|||
*/
|
||||
int SubsidySlowStartShift() const { return nSubsidySlowStartInterval / 2; }
|
||||
int nSubsidyHalvingInterval;
|
||||
int GetLastFoundersRewardBlockHeight() const {
|
||||
return nSubsidyHalvingInterval + SubsidySlowStartShift() - 1;
|
||||
}
|
||||
/** Used to check majorities for block version upgrade */
|
||||
int nMajorityEnforceBlockUpgrade;
|
||||
int nMajorityRejectBlockOutdated;
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
#include <gtest/gtest.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "utilmoneystr.h"
|
||||
#include "chainparams.h"
|
||||
#include "utilstrencodings.h"
|
||||
#include "zcash/Address.hpp"
|
||||
#include "wallet/wallet.h"
|
||||
#include "amount.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
// To run tests:
|
||||
// ./zcash-gtest --gtest_filter="founders_reward_test.*"
|
||||
|
||||
//
|
||||
// Enable this test to generate and print 48 testnet 2-of-3 multisig addresses.
|
||||
// The output can be copied into chainparams.cpp.
|
||||
//
|
||||
#if 0
|
||||
TEST(founders_reward_test, create_testnet_2of3multisig) {
|
||||
ECC_Start();
|
||||
SelectParams(CBaseChainParams::TESTNET);
|
||||
boost::filesystem::path temp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
|
||||
const std::string path = temp.native();
|
||||
bool fFirstRun;
|
||||
auto pWallet = std::make_shared<CWallet>(path);
|
||||
ASSERT_EQ(DB_LOAD_OK, pWallet->LoadWallet(fFirstRun));
|
||||
pWallet->TopUpKeyPool();
|
||||
|
||||
int numKeys = 48;
|
||||
std::vector<CPubKey> pubkeys;
|
||||
pubkeys.resize(3);
|
||||
CPubKey newKey;
|
||||
std::vector<std::string> addresses;
|
||||
for (int i=0; i<numKeys; i++) {
|
||||
ASSERT_TRUE(pWallet->GetKeyFromPool(newKey));
|
||||
pubkeys[0] = newKey;
|
||||
ASSERT_TRUE(pWallet->GetKeyFromPool(newKey));
|
||||
pubkeys[1] = newKey;
|
||||
ASSERT_TRUE(pWallet->GetKeyFromPool(newKey));
|
||||
pubkeys[2] = newKey;
|
||||
CScript result = GetScriptForMultisig(2, pubkeys);
|
||||
ASSERT_FALSE(result.size() > MAX_SCRIPT_ELEMENT_SIZE);
|
||||
CScriptID innerID(result);
|
||||
std::string address = CBitcoinAddress(innerID).ToString();
|
||||
addresses.push_back(address);
|
||||
}
|
||||
|
||||
// Print out the addresses, 4 on each line.
|
||||
std::string s = "vFoundersRewardAddress = {\n";
|
||||
int i=0;
|
||||
int colsPerRow = 4;
|
||||
ASSERT_TRUE(numKeys % colsPerRow == 0);
|
||||
int numRows = numKeys/colsPerRow;
|
||||
for (int row=0; row<numRows; row++) {
|
||||
s += " ";
|
||||
for (int col=0; col<colsPerRow; col++) {
|
||||
s += "\"" + addresses[i++] + "\", ";
|
||||
}
|
||||
s += "\n";
|
||||
}
|
||||
s += " };";
|
||||
std::cout << s << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#define NUM_TESTNET_FOUNDER_ADDRESSES 48
|
||||
|
||||
TEST(founders_reward_test, testnet) {
|
||||
SelectParams(CBaseChainParams::TESTNET);
|
||||
|
||||
CChainParams params = Params();
|
||||
|
||||
// For Testnet, the first address is derived from the old script.
|
||||
// FOUNDERS_REWARD_SCRIPT = a9146708e6670db0b950dac68031025cc5b63213a49187
|
||||
// address = 2N2e2FRfP9D1dRN1oRWkH7pbFM69eGNAuQ4
|
||||
EXPECT_EQ(params.GetFoundersRewardScript(1), ParseHex("a9146708e6670db0b950dac68031025cc5b63213a49187"));
|
||||
EXPECT_EQ(params.GetFoundersRewardAddress(1), "2N2e2FRfP9D1dRN1oRWkH7pbFM69eGNAuQ4");
|
||||
|
||||
int maxHeight = params.GetConsensus().GetLastFoundersRewardBlockHeight();
|
||||
|
||||
// If the block height parameter is out of bounds, there is an assert.
|
||||
EXPECT_DEATH(params.GetFoundersRewardScript(0), "nHeight");
|
||||
EXPECT_DEATH(params.GetFoundersRewardScript(maxHeight+1), "nHeight");
|
||||
EXPECT_DEATH(params.GetFoundersRewardAddress(0), "nHeight");
|
||||
EXPECT_DEATH(params.GetFoundersRewardAddress(maxHeight+1), "nHeight");
|
||||
|
||||
// Check that different addresses are used for testnet
|
||||
std::set<std::string> addresses;
|
||||
for (int i=1; i<=maxHeight; i++) {
|
||||
addresses.insert(params.GetFoundersRewardAddress(i));
|
||||
}
|
||||
ASSERT_TRUE(addresses.size()==NUM_TESTNET_FOUNDER_ADDRESSES);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#define NUM_MAINNET_FOUNDER_ADDRESSES 48
|
||||
|
||||
TEST(founders_reward_test, mainnet) {
|
||||
SelectParams(CBaseChainParams::MAIN);
|
||||
CChainParams params = Params();
|
||||
|
||||
int maxHeight = params.GetConsensus().GetLastFoundersRewardBlockHeight();
|
||||
std::set<std::string> addresses;
|
||||
for (int i=1; i<=maxHeight; i++) {
|
||||
addresses.insert(params.GetFoundersRewardAddress(i));
|
||||
}
|
||||
ASSERT_TRUE(addresses.size()==NUM_MAINNET_FOUNDER_ADDRESSES);
|
||||
}
|
||||
|
||||
|
||||
#define NUM_REGTEST_FOUNDER_ADDRESSES 1
|
||||
|
||||
TEST(founders_reward_test, regtest) {
|
||||
SelectParams(CBaseChainParams::REGTEST);
|
||||
CChainParams params = Params();
|
||||
|
||||
int maxHeight = params.GetConsensus().GetLastFoundersRewardBlockHeight();
|
||||
std::set<std::string> addresses;
|
||||
for (int i=1; i<=maxHeight; i++) {
|
||||
addresses.insert(params.GetFoundersRewardAddress(i));
|
||||
}
|
||||
ASSERT_TRUE(addresses.size()==NUM_REGTEST_FOUNDER_ADDRESSES);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Test that 10% founders reward is fully rewarded after the first halving and slow start shift.
|
||||
// On Mainnet, this would be 2,100,000 ZEC after 850,000 blocks (840,000 + 10,000).
|
||||
TEST(founders_reward_test, slow_start_subsidy) {
|
||||
SelectParams(CBaseChainParams::MAIN);
|
||||
CChainParams params = Params();
|
||||
|
||||
int maxHeight = params.GetConsensus().GetLastFoundersRewardBlockHeight();
|
||||
CAmount totalSubsidy = 0;
|
||||
for (int nHeight=1; nHeight<=maxHeight; nHeight++) {
|
||||
CAmount nSubsidy = GetBlockSubsidy(nHeight, params.GetConsensus())/ 5;
|
||||
totalSubsidy += nSubsidy;
|
||||
}
|
||||
|
||||
ASSERT_TRUE(totalSubsidy == MAX_MONEY/10.0);
|
||||
}
|
|
@ -3086,11 +3086,11 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
|
|||
// Coinbase transaction must include an output sending 20% of
|
||||
// the block reward to `FOUNDERS_REWARD_SCRIPT` until the first
|
||||
// subsidy halving block, with exception to the genesis block.
|
||||
if ((nHeight > 0) && (nHeight < consensusParams.nSubsidyHalvingInterval)) {
|
||||
if ((nHeight > 0) && (nHeight <= consensusParams.GetLastFoundersRewardBlockHeight())) {
|
||||
bool found = false;
|
||||
|
||||
BOOST_FOREACH(const CTxOut& output, block.vtx[0].vout) {
|
||||
if (output.scriptPubKey == ParseHex(FOUNDERS_REWARD_SCRIPT)) {
|
||||
if (output.scriptPubKey == Params().GetFoundersRewardScript(nHeight)) {
|
||||
if (output.nValue == (GetBlockSubsidy(nHeight, consensusParams) / 5)) {
|
||||
found = true;
|
||||
break;
|
||||
|
|
|
@ -47,9 +47,6 @@ class CValidationState;
|
|||
|
||||
struct CNodeStateStats;
|
||||
|
||||
// This is a 2-of-3 multisig P2SH
|
||||
static const char *FOUNDERS_REWARD_SCRIPT = "a9146708e6670db0b950dac68031025cc5b63213a49187";
|
||||
|
||||
/** Default for -blockmaxsize and -blockminsize, which control the range of sizes the mining code will create **/
|
||||
static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 750000;
|
||||
static const unsigned int DEFAULT_BLOCK_MIN_SIZE = 0;
|
||||
|
|
|
@ -335,17 +335,14 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
|
|||
txNew.vout[0].scriptPubKey = scriptPubKeyIn;
|
||||
txNew.vout[0].nValue = GetBlockSubsidy(nHeight, chainparams.GetConsensus());
|
||||
|
||||
if ((nHeight > 0) && (nHeight < chainparams.GetConsensus().nSubsidyHalvingInterval)) {
|
||||
if ((nHeight > 0) && (nHeight < chainparams.GetConsensus().GetLastFoundersRewardBlockHeight())) {
|
||||
// Founders reward is 20% of the block subsidy
|
||||
auto vFoundersReward = txNew.vout[0].nValue / 5;
|
||||
// Take some reward away from us
|
||||
txNew.vout[0].nValue -= vFoundersReward;
|
||||
|
||||
auto rewardScript = ParseHex(FOUNDERS_REWARD_SCRIPT);
|
||||
|
||||
// And give it to the founders
|
||||
txNew.vout.push_back(CTxOut(vFoundersReward, CScript(rewardScript.begin(),
|
||||
rewardScript.end())));
|
||||
txNew.vout.push_back(CTxOut(vFoundersReward, chainparams.GetFoundersRewardScript(nHeight)));
|
||||
}
|
||||
|
||||
// Add fees
|
||||
|
|
|
@ -786,7 +786,7 @@ Value getblocksubsidy(const Array& params, bool fHelp)
|
|||
|
||||
CAmount nReward = GetBlockSubsidy(nHeight, Params().GetConsensus());
|
||||
CAmount nFoundersReward = 0;
|
||||
if ((nHeight > 0) && (nHeight < Params().GetConsensus().nSubsidyHalvingInterval)) {
|
||||
if ((nHeight > 0) && (nHeight <= Params().GetConsensus().GetLastFoundersRewardBlockHeight())) {
|
||||
nFoundersReward = nReward/5;
|
||||
nReward -= nFoundersReward;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue