mirror of https://github.com/zcash/zips.git
411 lines
17 KiB
ReStructuredText
411 lines
17 KiB
ReStructuredText
::
|
|
|
|
ZIP: 207
|
|
Title: Funding Streams
|
|
Owners: Jack Grigg <str4d@electriccoin.co>
|
|
Daira Hopwood <daira@electriccoin.co>
|
|
Status: Proposed
|
|
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]_
|
|
Mainnet
|
|
The Zcash production network, as defined in the Zcash Protocol Specification. [#protocol]_
|
|
|
|
|
|
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, addresses for a given funding stream are
|
|
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-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)
|
|
|
|
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{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:
|
|
|
|
- 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.
|
|
|
|
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);
|
|
|
|
...
|
|
}
|
|
|
|
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] `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.1 or later <protocol/protocol.pdf>`_
|
|
.. [#protocol-subsidyconcepts] `Section 3.9: Block Subsidy and Founders' Reward. Zcash Protocol Specification, Version 2020.1.1 or later <protocol/protocol.pdf#subsidyconcepts>`_
|
|
.. [#protocol-constants] `Section 5.3: Constants. Zcash Protocol Specification, Version 2020.1.1 or later <protocol/protocol.pdf#constants>`_
|
|
.. [#protocol-subsidies] `Section 7.7: Calculation of Block Subsidy and Founders' Reward. Zcash Protocol Specification, Version 2020.1.1 or later <protocol/protocol.pdf#subsidies>`_
|
|
.. [#protocol-foundersreward] `Section 7.8: Payment of Founders' Reward. Zcash Protocol Specification, Version 2020.1.1 or later <protocol/protocol.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-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>`_
|