zips/zip-0207.rst

416 lines
17 KiB
ReStructuredText
Raw Normal View History

::
2019-01-24 17:47:55 -08:00
ZIP: 207
Title: Funding Streams
Owners: Jack Grigg <str4d@electriccoin.co>
Daira Hopwood <daira@electriccoin.co>
Status: Implemented (zcashd)
Category: Consensus
Created: 2019-01-04
License: MIT
Terminology
===========
The key words "MUST", "SHOULD", "SHOULD NOT", and "MAY" in this document are
to be interpreted as described in RFC 2119. [#RFC2119]_
The terms "block subsidy" and "halving" in this document are to be interpreted
as described in sections 3.9 and 7.7 of the Zcash Protocol Specification.
[#protocol-subsidyconcepts]_ [#protocol-subsidies]_
The terms "consensus branch" and "network upgrade" in this document are to be
interpreted as described in ZIP 200. [#zip-0200]_
The terms below are to be interpreted as follows:
Canopy
Code-name for the fifth Zcash network upgrade, also known as Network Upgrade 4.
Testnet
The Zcash test network, as defined in the Zcash Protocol Specification. [#protocol-networks]_
Mainnet
The Zcash production network, as defined in the Zcash Protocol Specification. [#protocol-networks]_
Abstract
========
This proposal specifies a mechanism to support funding streams, distributed
from a portion of the block subsidy for a specified range of block heights.
This is intended as a means of implementing the Zcash Development Fund,
using the funding stream definitions specified in ZIP 214 [#zip-0214]_. It
should be read in conjunction with ZIP 1014 [#zip-1014]_, which describes
the high-level requirements for that fund.
Motivation
==========
Motivation for the Zcash Development Fund is considered in ZIP 1014 [#zip-1014]_.
This ZIP 207 was originally proposed for the Blossom network upgrade, as a
means of splitting the original Founders' Reward into several streams. It was
then withdrawn when such splitting was judged to be unnecessary at the consensus
level. Since the capabilities of the funding stream mechanism match the
requirements for the Zcash Development Fund, the ZIP is being reintroduced
for that purpose in order to reuse specification, analysis, and implementation
effort.
Requirements
============
The primary requirement of this ZIP is to provide a mechanism for specifying
the funding streams that are used in ZIP 214 [#zip-0214]_ to implement the Zcash
Development Fund. It should be sufficiently expressive to handle both the main
three "slices" (ECC, ZF, and MG) defined in ZIP 1014 [#zip-1014]_, and also
(with additional funding stream definitions) the "direct grant option" described
in that ZIP.
As for the original Founders' Reward, a mechanism is provided to allow addresses
for a given funding stream to be changed on a roughly-monthly basis, so that keys
that are not yet needed may be kept off-line as a security measure.
Specification
=============
Definitions
-----------
We use the following constants and functions defined in [#protocol-constants]_,
[#protocol-diffadjustment]_, [#protocol-subsidies]_, and [#protocol-foundersreward]_:
- :math:`\mathsf{BlossomActivationHeight}`
- :math:`\mathsf{PostBlossomHalvingInterval}`
- :math:`\mathsf{Halving}(\mathsf{height})`
- :math:`\mathsf{BlockSubsidy}(\mathsf{height})`
- :math:`\mathsf{RedeemScriptHash}(\mathsf{height})`.
We also define the following function:
- :math:`\mathsf{HeightForHalving}(\mathsf{halving})`: Smallest :math:`\mathsf{height}` such that
:math:`\mathsf{Halving}(\mathsf{height}) = \mathsf{halving}`
Funding streams
---------------
A funding stream is defined by a block subsidy fraction (represented as a
numerator and a denominator), a start height (inclusive), and an end height
(exclusive).
By defining the issuance as a proportion of the total block subsidy, rather
than absolute zatoshis, this ZIP dovetails with any changes to both block
target spacing and issuance-per-block rates. Such a change occurred at the
Blossom network upgrade, for example. [#zip-0208]_
The value of a funding stream at a given block height is defined as:
.. math::
\mathsf{FundingStream[FUND].Value}(\mathsf{height}) =
\mathsf{floor}\left(
\frac{\mathsf{BlockSubsidy}(\mathsf{height}) \,\cdot\, \mathsf{FundingStream[FUND].ValueNumerator}}{\mathsf{FundingStream[FUND].ValueDenominator}}
\right)
2019-01-24 17:47:51 -08:00
An active funding stream at a given block height is defined as a funding
stream for which the block height is less than its end height, but not less
than its start height.
Each funding stream has an associated sequence of recipient addresses,
each of which MUST be either a transparent P2SH address or a Sapling address.
Each address is used for at most 1/48th of a halving interval, creating a
roughly-monthly sequence of funding periods. The address to be used for a
given block height is defined as follows:
.. math::
\begin{eqnarray*}
\mathsf{AddressChangeInterval} &=& \mathsf{PostBlossomHalvingInterval} / 48 \\
\mathsf{AddressPeriod}(\mathsf{height}) &=&
\mathsf{floor}\left(
{\small\frac{\mathsf{height} + \mathsf{PostBlossomHalvingInterval} - \mathsf{HeightForHalving}(1)}{\mathsf{AddressChangeInterval}}}
\right) \\
\mathsf{FundingStream[FUND].AddressIndex}(\mathsf{height}) &=&
\mathsf{AddressPeriod}(\mathsf{height}) - \\&&\hspace{2em} \mathsf{AddressPeriod}(\mathsf{FundingStream[FUND].StartHeight}) \\
\mathsf{FundingStream[FUND].Address}(\mathsf{height}) &=& \mathsf{FundingStream[FUND].Addresses[} \\&&\hspace{2em} \mathsf{FundingStream[FUND].AddressIndex}(\mathsf{height})\mathsf{]}
\end{eqnarray*}
This has the property that all active funding streams change the address they
are using on the same block height schedule, aligned to the height of the
first halving so that 48 funding periods fit cleanly within a halving
interval. This can be leveraged to simplify implementations, by batching the
necessary outputs for each funding period.
Below is a visual representation of how stream addresses align with funding
periods:
================================== ======== ======== ========
Example height Stream A Stream B Stream C
================================== ======== ======== ========
``AddressChangeInterval - 2`` A0
``AddressChangeInterval - 1`` A0
``AddressChangeInterval`` A1 B0 C0
``AddressChangeInterval + 1`` A1 B0 C0
\...
``2*AddressChangeInterval - 2`` A1 B0 C0
``2*AddressChangeInterval - 1`` A1 B0 C0
``2*AddressChangeInterval`` A2 C1
``2*AddressChangeInterval + 1`` A2 C1
\...
``PostBlossomHalvingInterval - 2`` A2 C1
``PostBlossomHalvingInterval - 1`` A2 C1
``PostBlossomHalvingInterval`` C2
``PostBlossomHalvingInterval + 1`` C2
================================== ======== ======== ========
On Mainnet, Canopy is planned to activate exactly at the point when the Founders'
Reward expires, at block height 1046400. On Testnet, there will be a shortened
Founders' Reward address period prior to Canopy activation.
Consensus rules
---------------
Prior to activation of the Canopy network upgrade, the existing consensus rule
for payment of the original Founders' Reward is enforced. [#protocol-foundersreward]_
Once the Canopy network upgrade activates:
2019-01-24 17:47:41 -08:00
- The existing consensus rule for payment of the Founders' Reward [#protocol-foundersreward]_
is no longer active.
(This would be the case under the preexisting consensus rules for Mainnet, but
not for Testnet.)
- The coinbase transaction in each block MUST contain at least one output per
active funding stream that pays the stream's value in the prescribed way to
the stream's recipient address for the block's height.
- The "prescribed way" to pay a transparent P2SH address is to use a standard
P2SH script of the form ``OP_HASH160 RedeemScriptHash(height) OP_EQUAL`` as
the ``scriptPubKey``.
- The "prescribed way" to pay a Sapling address is as defined in [#zip-0213]_.
That is, all Sapling outputs in coinbase transactions (including, but not
limited to, outputs for funding streams) MUST have valid note commitments
when recovered using a 32-byte array of zeroes as the outgoing viewing key.
In this case the note plaintext lead byte MUST be :math:`\mathbf{0x02}`, as
specified in [#zip-0212]_.
For the funding stream definitions to be activated at Canopy, see ZIP 214. [#zip-0214]_
Funding stream definitions can be added, changed, or deleted in ZIPs associated
with subsequent network upgrades, subject to the ZIP process. [#zip-0000]_
Example implementation
----------------------
.. code:: cpp
struct FundingPeriod {
std::vector<std::string> addresses,
uint64_t valueNumerator,
uint64_t valueDenominator,
int startHeight,
int endHeight,
};
enum FundingStream {
FS_ECC,
FS_ZF,
FS_MG,
MAX_FUNDING_STREAMS,
};
const auto FIRST_FUNDING_STREAM = FS_ECC;
struct Params {
...
int nFundingPeriodLength;
FundingPeriod vFundingPeriods[MAX_FUNDING_STREAMS];
...
}
void AddZIP207FundingStream(
Consensus::Params& params,
Consensus::FundingStream idx,
std::vector<std::string> addresses,
uint64_t valueNumerator,
uint64_t valueDenominator,
int startHeight,
int endHeight)
{
assert(valueNumerator < valueDenominator);
assert(valueNumerator < INT64_MAX / MAX_MONEY);
params.vFundingPeriods[idx].addresses = addresses;
params.vFundingPeriods[idx].valueNumerator = valueNumerator;
params.vFundingPeriods[idx].valueDenominator = valueDenominator;
params.vFundingPeriods[idx].startHeight = startHeight;
params.vFundingPeriods[idx].endHeight = endHeight;
assert(params.vFundingPeriods[idx].startHeight < params.vFundingPeriods[idx].endHeight);
};
CMainParams() {
...
consensus.nFundingPeriodLength = consensus.nSubsidyPostBlossomHalvingInterval / 48;
int devFundStartHeight = HeightForHalving(params, 1);
int devFundEndHeight = HeightForHalving(params, 2);
AddZIP207FundingStream(consensus, Consensus::FS_ECC, FS_ECC_ADDRESSES, 7, 100, devFundStartHeight, devFundEndHeight);
AddZIP207FundingStream(consensus, Consensus::FS_ZF, FS_ZF_ADDRESSES, 5, 100, devFundStartHeight, devFundEndHeight);
AddZIP207FundingStream(consensus, Consensus::FS_MG, FS_MG_ADDRESSES, 8, 100, devFundStartHeight, devFundEndHeight);
...
}
2019-01-24 17:47:25 -08:00
CScript FundingStreamRecipientAddress(
int nHeight,
const Consensus::Params& params,
Consensus::FundingStream idx)
{
assert(nHeight <= INT_MAX - params.nSubsidyPostBlossomHalvingInterval);
assert(params.vFundingPeriods[idx].startHeight <= INT_MAX - params.nSubsidyPostBlossomHalvingInterval);
int curPeriodNumerator = nHeight + params.nSubsidyPostBlossomHalvingInterval - HeightForHalving(params, 1);
int startPeriodNumerator = params.vFundingPeriods[idx].startHeight + params.nSubsidyPostBlossomHalvingInterval
- HeightForHalving(params, 1);
// Integer division is floor division for nonnegative integers in C++
assert(curPeriodNumerator >= 0);
assert(startPeriodNumerator >= 0);
auto curPeriod = curPeriodNumerator / params.nFundingPeriodLength;
auto startPeriod = startPeriodNumerator / params.nFundingPeriodLength;
auto addressIndex = curPeriod - startPeriod;
auto addresses = params.vFundingPeriods[idx].addresses;
assert(addressIndex >= 0 && addressIndex < addresses.size());
return addresses[addressIndex];
};
CAmount FundingStreamValue(
int nHeight,
const Consensus::Params& params,
Consensus::FundingStream idx)
{
// Integer division is floor division for nonnegative integers in C++
return CAmount((
GetBlockSubsidy(nHeight, params) * params.vFundingPeriods[idx].valueNumerator
) / params.vFundingPeriods[idx].valueDenominator);
}
std::set<std::pair<CScript, CAmount>> GetActiveFundingStreams(
int nHeight,
const Consensus::Params& params)
{
std::set<std::pair<CScript, CAmount>> requiredStreams;
for (int idx = Consensus::FIRST_FUNDING_STREAM; idx < Consensus::MAX_FUNDING_STREAMS; idx++) {
// Funding period is [startHeight, endHeight)
if (nHeight >= params.vFundingPeriods[idx].startHeight &&
nHeight < params.vFundingPeriods[idx].endHeight)
{
requiredStreams.insert(std::make_pair(
FundingStreamRecipientAddress(nHeight, params, idx),
FundingStreamValue(nHeight, params, idx));
}
}
return requiredStreams;
};
bool ContextualCheckBlock(...)
{
...
if (NetworkUpgradeActive(nHeight, consensusParams, Consensus::UPGRADE_CANOPY)) {
// Coinbase transaction must include outputs corresponding to the consensus
// funding streams active at the current block height.
auto requiredStreams = GetActiveFundingStreams(nHeight, consensusParams);
for (const CTxOut& output : block.vtx[0].vout) {
for (auto it = requiredStreams.begin(); it != requiredStreams.end(); ++it) {
if (output.scriptPubKey == it->first && output.nValue == it->second) {
requiredStreams.erase(it);
break;
}
}
}
if (!requiredStreams.empty()) {
return state.DoS(100, error("%s: funding stream missing", __func__),
REJECT_INVALID, "cb-funding-stream-missing");
}
} else {
// Coinbase transaction must include an output sending 20% of
// the block subsidy to a Founders' Reward script, until the last Founders'
// Reward block is reached, with exception of the genesis block.
// The last Founders' Reward block is defined as the block just before the
// first subsidy halving block.
if ((nHeight > 0) && (nHeight <= consensusParams.GetLastFoundersRewardBlockHeight())) {
bool found = false;
for (const CTxOut& output : block.vtx[0].vout) {
if (output.scriptPubKey == Params().GetFoundersRewardScriptAtHeight(nHeight)) {
if (output.nValue == (GetBlockSubsidy(nHeight, consensusParams) / 5)) {
found = true;
break;
}
}
}
if (!found) {
return state.DoS(100, error("%s: founders reward missing", __func__),
REJECT_INVALID, "cb-no-founders-reward");
}
}
}
...
}
Deployment
==========
This proposal is intended to be deployed with Canopy. [#zip-0251]_
Backward compatibility
======================
This proposal intentionally creates what is known as a "bilateral consensus
rule change". Use of this mechanism requires that all network participants
upgrade their software to a compatible version within the upgrade window.
Older software will treat post-upgrade blocks as invalid, and will follow any
pre-upgrade consensus branch that persists.
Reference Implementation
========================
TBC
References
==========
.. [#RFC2119] `RFC 2119: Key words for use in RFCs to Indicate Requirement Levels <https://www.rfc-editor.org/rfc/rfc2119.html>`_
.. [#protocol] `Zcash Protocol Specification, Version 2020.1.15 or later <protocol/canopy.pdf>`_
.. [#protocol-networks] `Zcash Protocol Specification, Version 2020.1.15. Section 3.11: Mainnet and Testnet <protocol/canopy.pdf#networks>`_
.. [#protocol-subsidyconcepts] `Zcash Protocol Specification, Version 2020.1.15. Section 3.9: Block Subsidy and Founders' Reward <protocol/canopy.pdf#subsidyconcepts>`_
.. [#protocol-constants] `Zcash Protocol Specification, Version 2020.1.15. Section 5.3: Constants <protocol/canopy.pdf#constants>`_
.. [#protocol-diffadjustment] `Zcash Protocol Specification, Version 2020.1.15. Section 7.6.3: Difficulty adjustment <protocol/canopy.pdf#diffadjustment>`_
.. [#protocol-subsidies] `Zcash Protocol Specification, Version 2020.1.15. Section 7.7: Calculation of Block Subsidy and Founders' Reward <protocol/canopy.pdf#subsidies>`_
.. [#protocol-foundersreward] `Zcash Protocol Specification, Version 2020.1.15. Section 7.8: Payment of Founders' Reward <protocol/canopy.pdf#foundersreward>`_
.. [#zip-0000] `ZIP 0: ZIP Process <zip-0000.rst>`_
.. [#zip-0200] `ZIP 200: Network Upgrade Mechanism <zip-0200.rst>`_
.. [#zip-0208] `ZIP 208: Shorter Block Target Spacing <zip-0208.rst>`_
.. [#zip-0212] `ZIP 212: Allow Recipient to Derive Sapling Ephemeral Secret from Note Plaintext <zip-0212.rst>`_
.. [#zip-0213] `ZIP 213: Shielded Coinbase <zip-0213.rst>`_
.. [#zip-0214] `ZIP 214: Consensus rules for a Zcash Development Fund <zip-0214.rst>`_
.. [#zip-0251] `ZIP 251: Deployment of the Canopy Network Upgrade <zip-0251.rst>`_
.. [#zip-1014] `ZIP 1014: Establishing a Dev Fund for ECC, ZF, and Major Grants <zip-1014.rst>`_