Skip to content
Merged
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
34284f8
create block subsidy rfc
oxarbitrage Oct 6, 2020
52bd951
update funding stream constants
oxarbitrage Oct 7, 2020
f68f127
Update xxxx-block-subsidy.md
oxarbitrage Oct 7, 2020
8b06235
Apply suggestions from code review
oxarbitrage Oct 8, 2020
ab88d50
add reference in miner subsidy
oxarbitrage Oct 8, 2020
2ace690
fix table
oxarbitrage Oct 8, 2020
e9661d3
Update xxxx-block-subsidy.md
oxarbitrage Oct 8, 2020
518e146
remove FundingStreamsHeightIntervals
oxarbitrage Oct 8, 2020
bd57900
remove `or 0` from function descriptions.
oxarbitrage Oct 8, 2020
c76d064
add links to subsidy categories
oxarbitrage Oct 8, 2020
90e5f36
fix design in Funding streams parameter constants
oxarbitrage Oct 8, 2020
92219a5
fix order in FUNDING_STREAM_RECEIVER_NUMERATORS
oxarbitrage Oct 8, 2020
135ea12
return `PayToScriptHash` in `funding_stream_address`
oxarbitrage Oct 8, 2020
3d6c392
fix definitions
oxarbitrage Oct 8, 2020
2b51780
change summary
oxarbitrage Oct 8, 2020
a4580ae
change motivation
oxarbitrage Oct 8, 2020
2732d07
change consensus rules intro
oxarbitrage Oct 8, 2020
f06ebbf
add types to constants
oxarbitrage Oct 8, 2020
c2f2a53
change `transaction_fees()` to `miner_fees()`
oxarbitrage Oct 8, 2020
8de943e
add shielded coinbase
oxarbitrage Oct 8, 2020
b84bf0b
move shielded coinbase link
oxarbitrage Oct 8, 2020
ad80a67
change return type of `funding_stream_address`
oxarbitrage Oct 9, 2020
e5083b2
Apply suggestions from code review
oxarbitrage Oct 12, 2020
421f1ea
fix table intervals
oxarbitrage Oct 12, 2020
589cef3
Use Range for FUNDING_STREAM_HEIGHT_RANGES
oxarbitrage Oct 12, 2020
134bc32
improve transparent value pool section
oxarbitrage Oct 12, 2020
9ef7dec
Apply suggestions from code review
oxarbitrage Oct 13, 2020
37fb20b
add a test plan section
oxarbitrage Oct 13, 2020
a8d32c5
fix extra paste
oxarbitrage Oct 13, 2020
d33027d
rename `subsidy_is_correct` to `subsidy_is_valid`
oxarbitrage Oct 13, 2020
5cff70b
add funding streams address constants
oxarbitrage Oct 13, 2020
bb933dd
fix funding stream addresses
oxarbitrage Oct 13, 2020
6c97019
add errors
oxarbitrage Oct 14, 2020
6304a81
remove ending dot for error descriptions
oxarbitrage Oct 15, 2020
5ee8efc
modify founders reward
oxarbitrage Oct 20, 2020
2c99a7f
Apply suggestions from code review
oxarbitrage Oct 23, 2020
617b3fe
change ECC to BP
oxarbitrage Oct 27, 2020
a30af1b
add note about state needed for transparent pool
oxarbitrage Nov 5, 2020
648e3f4
Apply suggestions from code review
oxarbitrage Nov 10, 2020
60eda9c
split constants
oxarbitrage Nov 10, 2020
978005e
Update book/src/dev/rfcs/xxxx-block-subsidy.md
oxarbitrage Nov 10, 2020
5eb45fc
Update book/src/dev/rfcs/xxxx-block-subsidy.md
oxarbitrage Nov 10, 2020
357406a
Tweak block subsidy RFC wording
teor2345 Feb 21, 2021
d3a3930
Spacing
teor2345 Feb 22, 2021
574a259
Clarify transaction fees definition
teor2345 Feb 22, 2021
9ae3321
Use consistent fee terminology
teor2345 Feb 22, 2021
4df5d82
Clarify transparent value pool
teor2345 Feb 22, 2021
ccd9118
Revise implementation plan based on latest priorities
teor2345 Feb 22, 2021
b074106
Change tests based on new priorities
teor2345 Feb 22, 2021
531de21
Fix markdown
teor2345 Feb 22, 2021
07634b4
Fix value pool description
teor2345 Feb 22, 2021
55c67a0
update some definitions
oxarbitrage Feb 25, 2021
60f1d8b
change block and miner subsidy definitions
oxarbitrage Feb 25, 2021
6e44892
fee pool definitions, fix all links to protocol
oxarbitrage Feb 25, 2021
ce81d5b
add reference to zip209
oxarbitrage Feb 25, 2021
29bd28e
add note for MG stream number of addresses
oxarbitrage Feb 25, 2021
8f91397
Be explicit in miner subsidy definition
oxarbitrage Feb 25, 2021
fc893e2
be more clear in NU definition
oxarbitrage Feb 25, 2021
62b3344
Add extra checks
oxarbitrage Feb 25, 2021
dda122c
Mark as draft and update ticket and PR links
teor2345 Mar 30, 2021
fe59379
Rename book/src/dev/rfcs/xxxx-block-subsidy.md to book/src/dev/rfcs/d…
teor2345 Mar 30, 2021
eae0dab
Add some TODOs from outstanding comments
teor2345 Mar 30, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
311 changes: 311 additions & 0 deletions book/src/dev/rfcs/drafts/xxxx-block-subsidy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,311 @@
- Feature Name: Block subsidy
- Start Date: 2020-10-05
- Design PR: [ZcashFoundation/zebra#1129](https://github.com/ZcashFoundation/zebra/pull/1129)
- Zebra Issue: [ZcashFoundation/zebra#338](https://github.com/ZcashFoundation/zebra/issues/338)

# Draft

Note: This is a draft Zebra RFC. See
[ZcashFoundation/zebra#338](https://github.com/ZcashFoundation/zebra/issues/338)
for more details.

# Summary
[summary]: #summary

Zebra manages [semantic verification](https://github.com/ZcashFoundation/zebra/blob/main/book/src/dev/rfcs/0002-parallel-verification.md#definitions) in the `zebra-consensus` crate, this is done for all incoming blocks. Inside each block the coinbase transaction is special, it holds the subsidy rewards that are paid to different participants (miners, founders, funding stream receivers). This RFC describes how to implement the needed calculations and verification for block subsidy and miner fees.

# Motivation
[motivation]: #motivation

All incoming blocks must be validated with the protocol specifications, there is no way to avoid including this logic in any Zcash protocol compatible implementation such as Zebra.

Block subsidy and miner fees are part of the protocol, the semantic verification of them apply to the coinbase transaction and they must be verified in Zebra.

# Definitions
[definitions]: #definitions

- **block subsidy**: Value created from mining composed of miner subsidy plus founders reward or funding stream if any of the 2 are active ([Subsidy Concepts](https://zips.z.cash/protocol/protocol.pdf#subsidyconcepts)).
- **coinbase transaction**: The first transaction in a block; this is the transaction that handles block subsidy.
- **founders reward**: The portion of the block subsidy that goes into a pre defined founder address in a single output.
- **funding streams**: The portion of the block subsidy that goes into one or more pre defined funding stream addresses. Payment is done with one output for each active funding stream of the block.
- **miner subsidy**: The portion of the block subsidy that goes into the miner of the block, excluding fees. The miner may split this amount into any number of outputs, to any addresses, including extra outputs to founders rewards and funding stream addresses.
- **network upgrade**: A rule change that creates a new consensus rule branch. Up-to-date nodes follow the new branch created by the network upgrade.
- **transaction fees**: The sum of the extra [transparent value pool](#transparent-value-pool-calculation) and shielded values, for all the transactions in a block. This amount can be spent by the miner in the coinbase transaction.

### Transaction fees calculation

There is a value pool inside each block called "[chain value pool balance](https://zips.z.cash/zip-0209#terminology)". Balance of that pool works as follows:

Transparent:

- `tx_in` adds to the pool.
- `tx_out` subtracts from the pool.

https://zips.z.cash/protocol/protocol.pdf#txnencodingandconsensus

Sprout:

- `vpub_new` values from `vJoinSplit` of the transaction add to the pool.
- `vpub_old` values from `vJoinSplit` of the transaction subtract from the pool.

https://zips.z.cash/protocol/protocol.pdf#joinsplitencodingandconsensus

Sapling:

- `valueBalance` adds to the pool. It itself is equal to `sum(SaplingSpends) - sum(SaplingOutputs)`, so it has the same functional effect as for transparent and Sprout.

https://zips.z.cash/protocol/protocol.pdf#txnencodingandconsensus

The balance rule is that this pool must have non-negative value (see [ZIP 209](https://zips.z.cash/zip-0209)), and its net value is the miner fee for the transaction. Non-negative transparent and block value pool balances are implied by the other consensus rules. Zebra should check or assert that these balances are non-negative, so invalid blocks don't pass verification. (And so that we guard against implementation bugs.)

**Note:** To compute the value pool we need blockchain state. The `tx_in` is always a reference to a previous transaction output, this is different from Sprout and Sapling pools where everything we need is in the same transaction. The details about how this is going to be implemented are outside of the scope of this RFC. They will be documented in a separate contextual validation RFC.

# Guide-level explanation
[guide-level-explanation]: #guide-level-explanation

In Zebra the consensus related code lives in the `zebra-consensus` crate. The block subsidy checks are implemented in `zebra-consensus`, in the following module:

- `zebra-consensus/src/block/subsidy/subsidy.rs`

Inside `zebra-consensus/src/block/subsidy/` the following submodules will be created:

- `general.rs`: General block reward functions and utilities.
- `founders_reward.rs`: Specific functions related to funders reward.
- `funding_streams.rs`: Specific functions for funding streams.

In addition to calculations the block subsidy requires constants defined in the protocol. The implementation will also create additional constants, all of them will live at:

- `zebra-consensus/src/parameters/subsidy.rs`

Checking functions for blocks are implemented at `zebra-consensus/src/block/check.rs` and they are called at `zebra-consensus/src/block.rs`. This follows the already existing structure for block validation in Zebra.

It is important to note that all blocks before Sapling are verified by the CheckpointVerifier, so they are not considered in this design. The following table will show what periods are included and what is not in this proposal:

| Height | Miner | Founder reward | Funding streams | Shielded Coinbase | Target Spacing |
|----------------------------------------|----------|----------------|-----------------|-------------------|----------------|
| ~Genesis~ | ~**0%**~ | ~**0%**~ | ~**0%**~ | ~**No**~ | ~**None**~ |
| ~(Genesis + 1)..Sapling~ | ~80%~ | ~20%~ | ~0%~ | ~No~ | ~150 seconds~ |
| Sapling..Blossom | 80% | 20% | 0% | No | 150 seconds |
| Blossom..Heartwood | 80% | 20% | 0% | No | 75 seconds |
| Heartwood..Canopy | 80% | 20% | 0% | Yes | 75 seconds |
| Canopy..Second Halving | 80% | 0% | 20% | Yes | 75 seconds |
| Second Halving.. | 100% | 0% | 0% | Yes | 75 seconds |

# Reference-level explanation
[reference-level-explanation]: #reference-level-explanation

Given the module structure proposed above, the block subsidy calculations from the protocol must be implemented. The final goal is to do semantic validation for each incoming block and make sure they pass the consensus rules.

To do the calculations and checks the following constants, types and functions need to be introduced:

## Constants

### General

- `SLOW_START_INTERVAL: Height`
- `SLOW_START_SHIFT: Height`
- `MAX_BLOCK_SUBSIDY: u64`
- `BLOSSOM_POW_TARGET_SPACING_RATIO: u64`
- `PRE_BLOSSOM_HALVING_INTERVAL: Height`
- `POST_BLOSSOM_HALVING_INTERVAL: Height`

### Founders Rewards

- `FOUNDERS_FRACTION_DIVISOR: u64`
- `FOUNDERS_ADDRESS_COUNT: u32`
- `FOUNDER_ADDRESSES_MAINNET: [&str; 48]`
- `FOUNDER_ADDRESSES_TESTNET: [&str; 48]`

### Funding streams

The design suggests to implement the parameters needed for funding streams as:

```
/// The funding stream receiver categories
pub enum FundingStreamReceiver {
BootstrapProject, // TODO: it might be clearer to call this ECC, no-one uses the legal name
ZcashFoundation,
MajorGrants,
}

/// The numerator for each funding stream receiving category
/// as described in [protocol specification §7.9.1][7.9.1].
///
/// [7.9.1]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams
const FUNDING_STREAM_RECEIVER_NUMERATORS: &[(FundingStreamReceiver, u64)] = &[
(FundingStreamReceiver::BootstrapProject, 7),
(FundingStreamReceiver::ZcashFoundation, 5),
(FundingStreamReceiver::MajorGrants, 8),
];

/// Denominator as described in [protocol specification §7.9.1][7.9.1].
///
/// [7.9.1]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams
pub const FUNDING_STREAM_RECEIVER_DENOMINATOR: u64 = 100;


/// Start and end Heights for funding streams
/// as described in [protocol specification §7.9.1][7.9.1].
///
/// [7.9.1]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams
const FUNDING_STREAM_HEIGHT_RANGES: &[(Network, Range<_>)] = &[
(Network::Mainnet, Height(1_046_400)..Height(2_726_400)),
(Network::Testnet, Height(1_028_500)..Height(2_796_000)),
];
```

Each `FundingStreamReceiver` for the mainnet must have 48 payment addresses associated with and 51 for the testnet.*

https://zips.z.cash/zip-0214#mainnet-recipient-addresses

The following constants are needed:

- `FUNDING_STREAM_BP_ADDRESSES_MAINNET: [&str; 48]`
- `FUNDING_STREAM_ZF_ADDRESSES_MAINNET: [&str; 48]`
- `FUNDING_STREAM_MG_ADDRESSES_MAINNET: [&str; 48]`
- `FUNDING_STREAM_BP_ADDRESSES_TESTNET: [&str; 51]`
- `FUNDING_STREAM_ZF_ADDRESSES_TESTNET: [&str; 51]`
- `FUNDING_STREAM_MG_ADDRESSES_TESTNET: [&str; 51]`

\* The MG stream is subject to change. [ZIP 1014](https://zips.z.cash/zip-1014#direct-grant-option) and [ZIP 214](https://zips.z.cash/zip-0214) explicitly permit splitting the stream to allow for direct grant recipients that could change the number of addresses. This are not considered in this spec.

Comment thread
teor2345 marked this conversation as resolved.
TODO: describe how we would handle this sort of change. For example: add an enum to `FUNDING_STREAM_HEIGHT_RANGES` for each different stream split.

## General subsidy

The block subsidy and other utility functions are inside the general subsidy category.

https://zips.z.cash/protocol/protocol.pdf#subsidyconcepts

https://zips.z.cash/protocol/protocol.pdf#subsidies

- `block_subsidy(Height, Network) -> Result<Amount<NonNegative>, Error>` - Total block subsidy.
- `miner_subsidy(Height, Network) -> Result<Amount<NonNegative>, Error>` - Miner portion.
- `find_output_with_amount(&Transaction, Amount<NonNegative>) -> Vec<transparent::Output>` - Outputs where value equal to Amount.
- `shielded_coinbase(Height, Network, &Transaction) -> Result<(), Error>` - Validate shielded coinbase rules.

## Founders reward

Only functions specific to calculation of founders reward.

https://zips.z.cash/protocol/protocol.pdf#foundersreward

- `founders_reward(Height, Network) -> Result<Amount<NonNegative>, Error>` - Founders reward portion for this block.
- `founders_address_change_interval() -> Height` - Calculates the founders reward change interval. `FounderAddressChangeInterval` in the protocol specs.
- `founders_reward_address(Height, Network) -> Result<PayToScriptHash, Error>` - Address of the receiver founder at this block. All specified founders reward addresses are transparent `zebra_chain::transparent:Address::PayToScriptHash` addresses. (Even after the shielded coinbase changes in ZIP-213, introduced in Heartwood.)
- `find_output_with_address(&Transaction, Address)` - Outputs where `lock_script` equal to `Address`.

## Funding streams

Only functions specific to the calculation of funding streams.

https://zips.z.cash/protocol/protocol.pdf#fundingstreams

https://zips.z.cash/zip-0207

https://zips.z.cash/zip-0214

- `funding_stream(height, newtork) -> Result<Amount<NonNegative>, Error>` - Funding stream portion for this block.
- `funding_stream_address(height, network) -> Result<FundingStreamAddress, Error>` - Address of the funding stream receiver at this block. The funding streams addresses can be transparent `zebra_chain::transparent:Address::PayToScriptHash` or `zebra_chain::sapling:Address` addresses.

## Consensus rules

`zebra-consensus/src/block.rs` will call the following function implemented inside `zebra-consensus/src/block/check.rs`:

`subsidy_is_valid(Network, &Block) -> Result<(), BlockError>`

`subsidy_is_valid()` will call individual functions(also implemented in `zebra-consensus/src/block/check.rs`) to verify the following consensus rules:

### 1 - Founders reward:

*[Pre-Canopy] A coinbase transaction at `height` **MUST** include at least one output that pays exactly `FoundersReward(height)` zatoshi with a standard P2SH script of the form `OP_HASH160 FounderRedeemScriptHash (height) OP_EQUAL` as its scriptPubKey.* https://zips.z.cash/protocol/protocol.pdf#foundersreward

We make use of the founders reward functions here. We get the amount of the reward with `founders_reward(height, network)` and the address for the reward at height with `founders_reward_address(height, network)`. Next we get a list of outputs that match the amount with the utility function `find_output_with_amount()`. Finally with this list, we check if any of the output scripts addresses matches our computed address.

### 2 - Funding stream:

*[Canopy onward] The coinbase transaction at `height` **MUST** contain at least one output per funding stream `fs` active at
height, that pays `fs.Value(height)` zatoshi in the prescribed way to the stream’s recipient address represented by `fs.AddressList` of `fs.AddressIndex(height)`.* https://zips.z.cash/protocol/protocol.pdf#fundingstreams

We make use of the funding streams functions here, similar to founders reward . We get the amount of the reward using `funding_stream(height, network)` and then the address with `funding_stream_address(height, network)`. Next we get a list of outputs that match the amount with the utility function `find_output_with_amount()`. Finally with this list, we check if any of the output scripts matches the address we have computed.

### 3 - Shielded coinbase:

*[Pre-Heartwood] A coinbase transaction MUST NOT have any JoinSplit descriptions, Spend descriptions, or Output descriptions.*

*[Heartwood onward] A coinbase transaction MUST NOT have any JoinSplit descriptions or Spend descriptions.*

*[Heartwood onward] The consensus rules applied to `valueBalance`, `vShieldedOutput`, and `bindingSig` in non-coinbase transactions MUST also be applied to coinbase transactions.*

https://zips.z.cash/zip-0213#specification

This rules are implemented inside `shielded_coinbase()`.

### Validation errors

A `SubsidyError` type will be created to handle validation of the above consensus rules:

```
pub enum SubsidyError {
#[error("not a coinbase transaction")]
NoCoinbase,

#[error("founders reward amount not found")]
FoundersRewardAmountNotFound,

#[error("founders reward address not found")]
FoundersRewardAddressNotFound,

#[error("funding stream amount not found")]
FundingStreamAmountNotFound,

#[error("funding stream address not found")]
FundingStreamAddressNotFound,

#[error("invalid shielded descriptions found")]
ShieldedDescriptionsInvalid,

#[error("broken rule in shielded transaction inside coinbase")]
ShieldedRuleBroken,
}
```
## Implementation Plan

Required:
1. Funding Streams Amounts (defer Shielded Coinbase)
2. Funding Streams Addresses
3. Shielded Coinbase for Funding Streams
4. Miner Subsidy Amounts
5. Shielded Coinbase for Miner Subsidy

Optional - might be replaced by a Canopy checkpoint:
1. Founders Reward Amounts
2. Founders Reward Addresses

The following contextual validation checks are out of scope for this RFC:
* Transaction Fees

Each stage should have code, unit tests, block test vector tests, and property tests, before moving on to the next stage. (A stage can be implemented in multiple PRs.)

## Test Plan

For each network(Mainnet, Testnet), calculation of subsidy amounts need a `Height` as input and will output different amounts according to it.

- Test all subsidy amount functions(`block_subsidy()`, `miner_subsidy()` and `funding_stream()`) against all network upgrade events of each network and make sure they return the expected amounts according to known outputs.

For each network, the address of the reward receiver on each block will depend on the `Height`.

- Test the subsidy address function (`funding_stream_address()`) against all network upgrade events of each network and make sure they return the expected addresses.

- Note: the founders reward tests are a low priority.

Validation tests will test the consensus rules using real blocks from `zebra-test` crate. For both networks, blocks for all network upgrades were added to the crate in [#1096](https://github.com/ZcashFoundation/zebra/pull/1096). Blocks containing shielded coinbase were also introduced at [#1116](https://github.com/ZcashFoundation/zebra/pull/1116)

- Test validation functions(`subsidy_is_valid()`) against all the blocks zebra haves available in the test vectors collection for both networks(`zebra_test::vectors::MAINNET_BLOCKS` and `zebra_test::vectors::TESTNET_BLOCKS`), all blocks should pass validation.

The validation errors at `SubsidyError` must be tested at least once.

- Create tests to trigger each error from `SubsidyError`.

- Create a property test for each consensus rule, which makes sure that the rule is satisfied by arbitrary (randomised) blocks and transactions
- Create or update `Arbitrary` trait implementations for blocks and transactions, as needed