Browse Source

Add founders reward to ChainParams.

Fix bug where subsidy slow shift was ignored.
pull/4/head
Simon 8 years ago
parent
commit
db0f931570
  1. 1
      src/Makefile.gtest.include
  2. 65
      src/chainparams.cpp
  3. 4
      src/chainparams.h
  4. 3
      src/consensus/params.h
  5. 148
      src/gtest/test_foundersreward.cpp
  6. 4
      src/main.cpp
  7. 3
      src/main.h
  8. 7
      src/miner.cpp
  9. 2
      src/rpcmining.cpp

1
src/Makefile.gtest.include

@ -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 \

65
src/chainparams.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;
}

4
src/chainparams.h

@ -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;
};
/**

3
src/consensus/params.h

@ -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;

148
src/gtest/test_foundersreward.cpp

@ -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);
}

4
src/main.cpp

@ -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;

3
src/main.h

@ -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;

7
src/miner.cpp

@ -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

2
src/rpcmining.cpp

@ -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…
Cancel
Save