Skip to content

feat(zcash_local_net): support configuring NU6.1 lockbox disbursements for regtest fixtures #243

@zancas

Description

@zancas

Today

zcash_local_net::validator::regtest_test_activation_heights puts NU6.1 at height 1000 to dodge zebrad's "missing lockbox disbursements for NU6.1 activation block" rejection. The downstream effect: any test whose chain reaches NU6.1 fails, so regtest fixtures effectively never activate it.

NU6.1 is mainnet-active. A regtest fixture that can't reach it is poor coverage.

Root cause (verified against Zebra source)

zebra-consensus/src/block/check.rs::subsidy_is_valid at the NU6.1 activation block:

if Some(height) == NetworkUpgrade::Nu6_1.activation_height(net) {
    let lockbox_disbursements = net.lockbox_disbursements(height);
    if lockbox_disbursements.is_empty() {
        Err(BlockError::Other(
            "missing lockbox disbursements for NU6.1 activation block".to_string(),
        ))?;
    }
    ...
}

Network::lockbox_disbursements(height) returns:

  • Mainnet / default Testnet: hardcoded ZIP-271 disbursement list.
  • Regtest: whatever was set via ParametersBuilder::with_lockbox_disbursements. Defaults to Vec::new() in Parameters::new_regtest.

The check is height-independent — there is no "minimum gap from NU6" rule. Heights 2, 3, 5, 10, 50 all fail the same way against the empty-default. zcashd has no equivalent check today, hence the validator divergence (probed empirically: launch_zcashd_with_nu6_1_at_height_2 passes; every launch_zebrad_with_nu6_1_at_height_* fails with submitblock returning "rejected").

Proposed change

A LockboxDisbursement value type and a ZebradConfig.lockbox_disbursements: Vec<LockboxDisbursement> field, serialized into Zebra's regtest config TOML at [[network.testnet_parameters.lockbox_disbursements]]. Default empty preserves today's behavior; a single dummy disbursement (1 zat to the standard regtest miner address) suffices to pass is_empty().

Schema, matching Zebra's ConfiguredLockboxDisbursement (zebra-chain/src/parameters/network/testnet.rs:124):

[[network.testnet_parameters.lockbox_disbursements]]
address = "tmF..."
amount = 1

Acceptance criteria

  • LockboxDisbursement type and ZebradConfig.lockbox_disbursements field exposed by zcash_local_net.
  • write_zebrad_config serializes the disbursement list when building the regtest config.
  • New integration test demonstrates Zebrad::launch + generate_blocks past NU6.1 activation when the field is populated.
  • Existing regtest tests (with the field defaulting empty) continue to pass.

Open question

Whether a single dummy disbursement satisfies subsidy_is_valid end-to-end, or whether the activation-block coinbase must also emit matching outputs paying each disbursement address. Settled empirically by the proof-of-life test in the linked PR.

Out of scope

  • Coordinating the matching change in zaino (zaino-common::ZEBRAD_DEFAULT_ACTIVATION_HEIGHTS). Tracked as a downstream issue — see cross-reference.

Cross-reference

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions